root/drivers/scsi/esas2r/esas2r_flash.c

/*
 *  linux/drivers/scsi/esas2r/esas2r_flash.c
 *      For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
 *
 *  Copyright (c) 2001-2013 ATTO Technology, Inc.
 *  (mailto:linuxdrivers@attotech.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * NO WARRANTY
 * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
 * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
 * solely responsible for determining the appropriateness of using and
 * distributing the Program and assumes all risks associated with its
 * exercise of rights under this Agreement, including but not limited to
 * the risks and costs of program errors, damage to or loss of data,
 * programs or equipment, and unavailability or interruption of operations.
 *
 * DISCLAIMER OF LIABILITY
 * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
 * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 */

#include "esas2r.h"

/* local macro defs */
#define esas2r_nvramcalc_cksum(n)     \
        (esas2r_calc_byte_cksum((u8 *)(n), sizeof(struct esas2r_sas_nvram), \
                                SASNVR_CKSUM_SEED))
#define esas2r_nvramcalc_xor_cksum(n)  \
        (esas2r_calc_byte_xor_cksum((u8 *)(n), \
                                    sizeof(struct esas2r_sas_nvram), 0))

#define ESAS2R_FS_DRVR_VER 2

static struct esas2r_sas_nvram default_sas_nvram = {
        { 'E',  'S',  'A',  'S'                      }, /* signature          */
        SASNVR_VERSION,                                 /* version            */
        0,                                              /* checksum           */
        31,                                             /* max_lun_for_target */
        SASNVR_PCILAT_MAX,                              /* pci_latency        */
        SASNVR1_BOOT_DRVR,                              /* options1           */
        SASNVR2_HEARTBEAT   | SASNVR2_SINGLE_BUS        /* options2           */
        | SASNVR2_SW_MUX_CTRL,
        SASNVR_COAL_DIS,                                /* int_coalescing     */
        SASNVR_CMDTHR_NONE,                             /* cmd_throttle       */
        3,                                              /* dev_wait_time      */
        1,                                              /* dev_wait_count     */
        0,                                              /* spin_up_delay      */
        0,                                              /* ssp_align_rate     */
        { 0x50, 0x01, 0x08, 0x60,                       /* sas_addr           */
          0x00, 0x00, 0x00, 0x00 },
        { SASNVR_SPEED_AUTO },                          /* phy_speed          */
        { SASNVR_MUX_DISABLED },                        /* SAS multiplexing   */
        { 0 },                                          /* phy_flags          */
        SASNVR_SORT_SAS_ADDR,                           /* sort_type          */
        3,                                              /* dpm_reqcmd_lmt     */
        3,                                              /* dpm_stndby_time    */
        0,                                              /* dpm_active_time    */
        { 0 },                                          /* phy_target_id      */
        SASNVR_VSMH_DISABLED,                           /* virt_ses_mode      */
        SASNVR_RWM_DEFAULT,                             /* read_write_mode    */
        0,                                              /* link down timeout  */
        { 0 }                                           /* reserved           */
};

static u8 cmd_to_fls_func[] = {
        0xFF,
        VDA_FLASH_READ,
        VDA_FLASH_BEGINW,
        VDA_FLASH_WRITE,
        VDA_FLASH_COMMIT,
        VDA_FLASH_CANCEL
};

static u8 esas2r_calc_byte_xor_cksum(u8 *addr, u32 len, u8 seed)
{
        u32 cksum = seed;
        u8 *p = (u8 *)&cksum;

        while (len) {
                if (((uintptr_t)addr & 3) == 0)
                        break;

                cksum = cksum ^ *addr;
                addr++;
                len--;
        }
        while (len >= sizeof(u32)) {
                cksum = cksum ^ *(u32 *)addr;
                addr += 4;
                len -= 4;
        }
        while (len--) {
                cksum = cksum ^ *addr;
                addr++;
        }
        return p[0] ^ p[1] ^ p[2] ^ p[3];
}

static u8 esas2r_calc_byte_cksum(void *addr, u32 len, u8 seed)
{
        u8 *p = (u8 *)addr;
        u8 cksum = seed;

        while (len--)
                cksum = cksum + p[len];
        return cksum;
}

/* Interrupt callback to process FM API write requests. */
static void esas2r_fmapi_callback(struct esas2r_adapter *a,
                                  struct esas2r_request *rq)
{
        struct atto_vda_flash_req *vrq = &rq->vrq->flash;
        struct esas2r_flash_context *fc =
                (struct esas2r_flash_context *)rq->interrupt_cx;

        if (rq->req_stat == RS_SUCCESS) {
                /* Last request was successful.  See what to do now. */
                switch (vrq->sub_func) {
                case VDA_FLASH_BEGINW:
                        if (fc->sgc.cur_offset == NULL)
                                goto commit;

                        vrq->sub_func = VDA_FLASH_WRITE;
                        rq->req_stat = RS_PENDING;
                        break;

                case VDA_FLASH_WRITE:
commit:
                        vrq->sub_func = VDA_FLASH_COMMIT;
                        rq->req_stat = RS_PENDING;
                        rq->interrupt_cb = fc->interrupt_cb;
                        break;

                default:
                        break;
                }
        }

        if (rq->req_stat != RS_PENDING)
                /*
                 * All done. call the real callback to complete the FM API
                 * request.  We should only get here if a BEGINW or WRITE
                 * operation failed.
                 */
                (*fc->interrupt_cb)(a, rq);
}

/*
 * Build a flash request based on the flash context.  The request status
 * is filled in on an error.
 */
static void build_flash_msg(struct esas2r_adapter *a,
                            struct esas2r_request *rq)
{
        struct esas2r_flash_context *fc =
                (struct esas2r_flash_context *)rq->interrupt_cx;
        struct esas2r_sg_context *sgc = &fc->sgc;
        u8 cksum = 0;

        /* calculate the checksum */
        if (fc->func == VDA_FLASH_BEGINW) {
                if (sgc->cur_offset)
                        cksum = esas2r_calc_byte_xor_cksum(sgc->cur_offset,
                                                           sgc->length,
                                                           0);
                rq->interrupt_cb = esas2r_fmapi_callback;
        } else {
                rq->interrupt_cb = fc->interrupt_cb;
        }
        esas2r_build_flash_req(a,
                               rq,
                               fc->func,
                               cksum,
                               fc->flsh_addr,
                               sgc->length);

        esas2r_rq_free_sg_lists(rq, a);

        /*
         * remember the length we asked for.  we have to keep track of
         * the current amount done so we know how much to compare when
         * doing the verification phase.
         */
        fc->curr_len = fc->sgc.length;

        if (sgc->cur_offset) {
                /* setup the S/G context to build the S/G table  */
                esas2r_sgc_init(sgc, a, rq, &rq->vrq->flash.data.sge[0]);

                if (!esas2r_build_sg_list(a, rq, sgc)) {
                        rq->req_stat = RS_BUSY;
                        return;
                }
        } else {
                fc->sgc.length = 0;
        }

        /* update the flsh_addr to the next one to write to  */
        fc->flsh_addr += fc->curr_len;
}

/* determine the method to process the flash request */
static bool load_image(struct esas2r_adapter *a, struct esas2r_request *rq)
{
        /*
         * assume we have more to do.  if we return with the status set to
         * RS_PENDING, FM API tasks will continue.
         */
        rq->req_stat = RS_PENDING;
        if (test_bit(AF_DEGRADED_MODE, &a->flags))
                /* not supported for now */;
        else
                build_flash_msg(a, rq);

        return rq->req_stat == RS_PENDING;
}

/*  boot image fixer uppers called before downloading the image. */
static void fix_bios(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
{
        struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_BIOS];
        struct esas2r_pc_image *pi;
        struct esas2r_boot_header *bh;

        pi = (struct esas2r_pc_image *)((u8 *)fi + ch->image_offset);
        bh =
                (struct esas2r_boot_header *)((u8 *)pi +
                                              le16_to_cpu(pi->header_offset));
        bh->device_id = cpu_to_le16(a->pcid->device);

        /* Recalculate the checksum in the PNP header if there  */
        if (pi->pnp_offset) {
                u8 *pnp_header_bytes =
                        ((u8 *)pi + le16_to_cpu(pi->pnp_offset));

                /* Identifier - dword that starts at byte 10 */
                *((u32 *)&pnp_header_bytes[10]) =
                        cpu_to_le32(MAKEDWORD(a->pcid->subsystem_vendor,
                                              a->pcid->subsystem_device));

                /* Checksum - byte 9 */
                pnp_header_bytes[9] -= esas2r_calc_byte_cksum(pnp_header_bytes,
                                                              32, 0);
        }

        /* Recalculate the checksum needed by the PC */
        pi->checksum = pi->checksum -
                       esas2r_calc_byte_cksum((u8 *)pi, ch->length, 0);
}

static void fix_efi(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
{
        struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_EFI];
        u32 len = ch->length;
        u32 offset = ch->image_offset;
        struct esas2r_efi_image *ei;
        struct esas2r_boot_header *bh;

        while (len) {
                u32 thislen;

                ei = (struct esas2r_efi_image *)((u8 *)fi + offset);
                bh = (struct esas2r_boot_header *)((u8 *)ei +
                                                   le16_to_cpu(
                                                           ei->header_offset));
                bh->device_id = cpu_to_le16(a->pcid->device);
                thislen = (u32)le16_to_cpu(bh->image_length) * 512;

                if (thislen > len)
                        break;

                len -= thislen;
                offset += thislen;
        }
}

/* Complete a FM API request with the specified status. */
static bool complete_fmapi_req(struct esas2r_adapter *a,
                               struct esas2r_request *rq, u8 fi_stat)
{
        struct esas2r_flash_context *fc =
                (struct esas2r_flash_context *)rq->interrupt_cx;
        struct esas2r_flash_img *fi = fc->fi;

        fi->status = fi_stat;
        fi->driver_error = rq->req_stat;
        rq->interrupt_cb = NULL;
        rq->req_stat = RS_SUCCESS;

        if (fi_stat != FI_STAT_IMG_VER)
                memset(fc->scratch, 0, FM_BUF_SZ);

        esas2r_enable_heartbeat(a);
        clear_bit(AF_FLASH_LOCK, &a->flags);
        return false;
}

/* Process each phase of the flash download process. */
static void fw_download_proc(struct esas2r_adapter *a,
                             struct esas2r_request *rq)
{
        struct esas2r_flash_context *fc =
                (struct esas2r_flash_context *)rq->interrupt_cx;
        struct esas2r_flash_img *fi = fc->fi;
        struct esas2r_component_header *ch;
        u32 len;
        u8 *p, *q;

        /* If the previous operation failed, just return. */
        if (rq->req_stat != RS_SUCCESS)
                goto error;

        /*
         * If an upload just completed and the compare length is non-zero,
         * then we just read back part of the image we just wrote.  verify the
         * section and continue reading until the entire image is verified.
         */
        if (fc->func == VDA_FLASH_READ
            && fc->cmp_len) {
                ch = &fi->cmp_hdr[fc->comp_typ];

                p = fc->scratch;
                q = (u8 *)fi                    /* start of the whole gob     */
                    + ch->image_offset          /* start of the current image */
                    + ch->length                /* end of the current image   */
                    - fc->cmp_len;              /* where we are now           */

                /*
                 * NOTE - curr_len is the exact count of bytes for the read
                 *        even when the end is read and its not a full buffer
                 */
                for (len = fc->curr_len; len; len--)
                        if (*p++ != *q++)
                                goto error;

                fc->cmp_len -= fc->curr_len; /* # left to compare    */

                /* Update fc and determine the length for the next upload */
                if (fc->cmp_len > FM_BUF_SZ)
                        fc->sgc.length = FM_BUF_SZ;
                else
                        fc->sgc.length = fc->cmp_len;

                fc->sgc.cur_offset = fc->sgc_offset +
                                     ((u8 *)fc->scratch - (u8 *)fi);
        }

        /*
         * This code uses a 'while' statement since the next component may
         * have a length = zero.  This can happen since some components are
         * not required.  At the end of this 'while' we set up the length
         * for the next request and therefore sgc.length can be = 0.
         */
        while (fc->sgc.length == 0) {
                ch = &fi->cmp_hdr[fc->comp_typ];

                switch (fc->task) {
                case FMTSK_ERASE_BOOT:
                        /* the BIOS image is written next */
                        ch = &fi->cmp_hdr[CH_IT_BIOS];
                        if (ch->length == 0)
                                goto no_bios;

                        fc->task = FMTSK_WRTBIOS;
                        fc->func = VDA_FLASH_BEGINW;
                        fc->comp_typ = CH_IT_BIOS;
                        fc->flsh_addr = FLS_OFFSET_BOOT;
                        fc->sgc.length = ch->length;
                        fc->sgc.cur_offset = fc->sgc_offset +
                                             ch->image_offset;
                        break;

                case FMTSK_WRTBIOS:
                        /*
                         * The BIOS image has been written - read it and
                         * verify it
                         */
                        fc->task = FMTSK_READBIOS;
                        fc->func = VDA_FLASH_READ;
                        fc->flsh_addr = FLS_OFFSET_BOOT;
                        fc->cmp_len = ch->length;
                        fc->sgc.length = FM_BUF_SZ;
                        fc->sgc.cur_offset = fc->sgc_offset
                                             + ((u8 *)fc->scratch -
                                                (u8 *)fi);
                        break;

                case FMTSK_READBIOS:
no_bios:
                        /*
                         * Mark the component header status for the image
                         * completed
                         */
                        ch->status = CH_STAT_SUCCESS;

                        /* The MAC image is written next */
                        ch = &fi->cmp_hdr[CH_IT_MAC];
                        if (ch->length == 0)
                                goto no_mac;

                        fc->task = FMTSK_WRTMAC;
                        fc->func = VDA_FLASH_BEGINW;
                        fc->comp_typ = CH_IT_MAC;
                        fc->flsh_addr = FLS_OFFSET_BOOT
                                        + fi->cmp_hdr[CH_IT_BIOS].length;
                        fc->sgc.length = ch->length;
                        fc->sgc.cur_offset = fc->sgc_offset +
                                             ch->image_offset;
                        break;

                case FMTSK_WRTMAC:
                        /* The MAC image has been written - read and verify */
                        fc->task = FMTSK_READMAC;
                        fc->func = VDA_FLASH_READ;
                        fc->flsh_addr -= ch->length;
                        fc->cmp_len = ch->length;
                        fc->sgc.length = FM_BUF_SZ;
                        fc->sgc.cur_offset = fc->sgc_offset
                                             + ((u8 *)fc->scratch -
                                                (u8 *)fi);
                        break;

                case FMTSK_READMAC:
no_mac:
                        /*
                         * Mark the component header status for the image
                         * completed
                         */
                        ch->status = CH_STAT_SUCCESS;

                        /* The EFI image is written next */
                        ch = &fi->cmp_hdr[CH_IT_EFI];
                        if (ch->length == 0)
                                goto no_efi;

                        fc->task = FMTSK_WRTEFI;
                        fc->func = VDA_FLASH_BEGINW;
                        fc->comp_typ = CH_IT_EFI;
                        fc->flsh_addr = FLS_OFFSET_BOOT
                                        + fi->cmp_hdr[CH_IT_BIOS].length
                                        + fi->cmp_hdr[CH_IT_MAC].length;
                        fc->sgc.length = ch->length;
                        fc->sgc.cur_offset = fc->sgc_offset +
                                             ch->image_offset;
                        break;

                case FMTSK_WRTEFI:
                        /* The EFI image has been written - read and verify */
                        fc->task = FMTSK_READEFI;
                        fc->func = VDA_FLASH_READ;
                        fc->flsh_addr -= ch->length;
                        fc->cmp_len = ch->length;
                        fc->sgc.length = FM_BUF_SZ;
                        fc->sgc.cur_offset = fc->sgc_offset
                                             + ((u8 *)fc->scratch -
                                                (u8 *)fi);
                        break;

                case FMTSK_READEFI:
no_efi:
                        /*
                         * Mark the component header status for the image
                         * completed
                         */
                        ch->status = CH_STAT_SUCCESS;

                        /* The CFG image is written next */
                        ch = &fi->cmp_hdr[CH_IT_CFG];

                        if (ch->length == 0)
                                goto no_cfg;
                        fc->task = FMTSK_WRTCFG;
                        fc->func = VDA_FLASH_BEGINW;
                        fc->comp_typ = CH_IT_CFG;
                        fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
                        fc->sgc.length = ch->length;
                        fc->sgc.cur_offset = fc->sgc_offset +
                                             ch->image_offset;
                        break;

                case FMTSK_WRTCFG:
                        /* The CFG image has been written - read and verify */
                        fc->task = FMTSK_READCFG;
                        fc->func = VDA_FLASH_READ;
                        fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
                        fc->cmp_len = ch->length;
                        fc->sgc.length = FM_BUF_SZ;
                        fc->sgc.cur_offset = fc->sgc_offset
                                             + ((u8 *)fc->scratch -
                                                (u8 *)fi);
                        break;

                case FMTSK_READCFG:
no_cfg:
                        /*
                         * Mark the component header status for the image
                         * completed
                         */
                        ch->status = CH_STAT_SUCCESS;

                        /*
                         * The download is complete.  If in degraded mode,
                         * attempt a chip reset.
                         */
                        if (test_bit(AF_DEGRADED_MODE, &a->flags))
                                esas2r_local_reset_adapter(a);

                        a->flash_ver = fi->cmp_hdr[CH_IT_BIOS].version;
                        esas2r_print_flash_rev(a);

                        /* Update the type of boot image on the card */
                        memcpy(a->image_type, fi->rel_version,
                               sizeof(fi->rel_version));
                        complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
                        return;
                }

                /* If verifying, don't try reading more than what's there */
                if (fc->func == VDA_FLASH_READ
                    && fc->sgc.length > fc->cmp_len)
                        fc->sgc.length = fc->cmp_len;
        }

        /* Build the request to perform the next action */
        if (!load_image(a, rq)) {
error:
                if (fc->comp_typ < fi->num_comps) {
                        ch = &fi->cmp_hdr[fc->comp_typ];
                        ch->status = CH_STAT_FAILED;
                }

                complete_fmapi_req(a, rq, FI_STAT_FAILED);
        }
}

/* Determine the flash image adaptyp for this adapter */
static u8 get_fi_adap_type(struct esas2r_adapter *a)
{
        u8 type;

        /* use the device ID to get the correct adap_typ for this HBA */
        switch (a->pcid->device) {
        case ATTO_DID_INTEL_IOP348:
                type = FI_AT_SUN_LAKE;
                break;

        case ATTO_DID_MV_88RC9580:
        case ATTO_DID_MV_88RC9580TS:
        case ATTO_DID_MV_88RC9580TSE:
        case ATTO_DID_MV_88RC9580TL:
                type = FI_AT_MV_9580;
                break;

        default:
                type = FI_AT_UNKNWN;
                break;
        }

        return type;
}

/* Size of config + copyright + flash_ver images, 0 for failure. */
static u32 chk_cfg(u8 *cfg, u32 length, u32 *flash_ver)
{
        u16 *pw = (u16 *)cfg - 1;
        u32 sz = 0;
        u32 len = length;

        if (len == 0)
                len = FM_BUF_SZ;

        if (flash_ver)
                *flash_ver = 0;

        while (true) {
                u16 type;
                u16 size;

                type = le16_to_cpu(*pw--);
                size = le16_to_cpu(*pw--);

                if (type != FBT_CPYR
                    && type != FBT_SETUP
                    && type != FBT_FLASH_VER)
                        break;

                if (type == FBT_FLASH_VER
                    && flash_ver)
                        *flash_ver = le32_to_cpu(*(u32 *)(pw - 1));

                sz += size + (2 * sizeof(u16));
                pw -= size / sizeof(u16);

                if (sz > len - (2 * sizeof(u16)))
                        break;
        }

        /* See if we are comparing the size to the specified length */
        if (length && sz != length)
                return 0;

        return sz;
}

/* Verify that the boot image is valid */
static u8 chk_boot(u8 *boot_img, u32 length)
{
        struct esas2r_boot_image *bi = (struct esas2r_boot_image *)boot_img;
        u16 hdroffset = le16_to_cpu(bi->header_offset);
        struct esas2r_boot_header *bh;

        if (bi->signature != le16_to_cpu(0xaa55)
            || (long)hdroffset >
            (long)(65536L - sizeof(struct esas2r_boot_header))
            || (hdroffset & 3)
            || (hdroffset < sizeof(struct esas2r_boot_image))
            || ((u32)hdroffset + sizeof(struct esas2r_boot_header) > length))
                return 0xff;

        bh = (struct esas2r_boot_header *)((char *)bi + hdroffset);

        if (bh->signature[0] != 'P'
            || bh->signature[1] != 'C'
            || bh->signature[2] != 'I'
            || bh->signature[3] != 'R'
            || le16_to_cpu(bh->struct_length) <
            (u16)sizeof(struct esas2r_boot_header)
            || bh->class_code[2] != 0x01
            || bh->class_code[1] != 0x04
            || bh->class_code[0] != 0x00
            || (bh->code_type != CODE_TYPE_PC
                && bh->code_type != CODE_TYPE_OPEN
                && bh->code_type != CODE_TYPE_EFI))
                return 0xff;

        return bh->code_type;
}

/* The sum of all the WORDS of the image */
static u16 calc_fi_checksum(struct esas2r_flash_context *fc)
{
        struct esas2r_flash_img *fi = fc->fi;
        u16 cksum;
        u32 len;
        u16 *pw;

        for (len = (fi->length - fc->fi_hdr_len) / 2,
             pw = (u16 *)((u8 *)fi + fc->fi_hdr_len),
             cksum = 0;
             len;
             len--, pw++)
                cksum = cksum + le16_to_cpu(*pw);

        return cksum;
}

/*
 * Verify the flash image structure.  The following verifications will
 * be performed:
 *              1)  verify the fi_version is correct
 *              2)  verify the checksum of the entire image.
 *              3)  validate the adap_typ, action and length fields.
 *              4)  validate each component header. check the img_type and
 *                  length fields
 *              5)  validate each component image.  validate signatures and
 *                  local checksums
 */
static bool verify_fi(struct esas2r_adapter *a,
                      struct esas2r_flash_context *fc)
{
        struct esas2r_flash_img *fi = fc->fi;
        u8 type;
        bool imgerr;
        u16 i;
        u32 len;
        struct esas2r_component_header *ch;

        /* Verify the length - length must even since we do a word checksum */
        len = fi->length;

        if ((len & 1)
            || len < fc->fi_hdr_len) {
                fi->status = FI_STAT_LENGTH;
                return false;
        }

        /* Get adapter type and verify type in flash image */
        type = get_fi_adap_type(a);
        if ((type == FI_AT_UNKNWN) || (fi->adap_typ != type)) {
                fi->status = FI_STAT_ADAPTYP;
                return false;
        }

        /*
         * Loop through each component and verify the img_type and length
         * fields.  Keep a running count of the sizes sooze we can verify total
         * size to additive size.
         */
        imgerr = false;

        for (i = 0, len = 0, ch = fi->cmp_hdr;
             i < fi->num_comps;
             i++, ch++) {
                bool cmperr = false;

                /*
                 * Verify that the component header has the same index as the
                 * image type.  The headers must be ordered correctly
                 */
                if (i != ch->img_type) {
                        imgerr = true;
                        ch->status = CH_STAT_INVALID;
                        continue;
                }

                switch (ch->img_type) {
                case CH_IT_BIOS:
                        type = CODE_TYPE_PC;
                        break;

                case CH_IT_MAC:
                        type = CODE_TYPE_OPEN;
                        break;

                case CH_IT_EFI:
                        type = CODE_TYPE_EFI;
                        break;
                }

                switch (ch->img_type) {
                case CH_IT_FW:
                case CH_IT_NVR:
                        break;

                case CH_IT_BIOS:
                case CH_IT_MAC:
                case CH_IT_EFI:
                        if (ch->length & 0x1ff)
                                cmperr = true;

                        /* Test if component image is present  */
                        if (ch->length == 0)
                                break;

                        /* Image is present - verify the image */
                        if (chk_boot((u8 *)fi + ch->image_offset, ch->length)
                            != type)
                                cmperr = true;

                        break;

                case CH_IT_CFG:

                        /* Test if component image is present */
                        if (ch->length == 0) {
                                cmperr = true;
                                break;
                        }

                        /* Image is present - verify the image */
                        if (!chk_cfg((u8 *)fi + ch->image_offset + ch->length,
                                     ch->length, NULL))
                                cmperr = true;

                        break;

                default:

                        fi->status = FI_STAT_UNKNOWN;
                        return false;
                }

                if (cmperr) {
                        imgerr = true;
                        ch->status = CH_STAT_INVALID;
                } else {
                        ch->status = CH_STAT_PENDING;
                        len += ch->length;
                }
        }

        if (imgerr) {
                fi->status = FI_STAT_MISSING;
                return false;
        }

        /* Compare fi->length to the sum of ch->length fields */
        if (len != fi->length - fc->fi_hdr_len) {
                fi->status = FI_STAT_LENGTH;
                return false;
        }

        /* Compute the checksum - it should come out zero */
        if (fi->checksum != calc_fi_checksum(fc)) {
                fi->status = FI_STAT_CHKSUM;
                return false;
        }

        return true;
}

/* Fill in the FS IOCTL response data from a completed request. */
static void esas2r_complete_fs_ioctl(struct esas2r_adapter *a,
                                     struct esas2r_request *rq)
{
        struct esas2r_ioctl_fs *fs =
                (struct esas2r_ioctl_fs *)rq->interrupt_cx;

        if (rq->vrq->flash.sub_func == VDA_FLASH_COMMIT)
                esas2r_enable_heartbeat(a);

        fs->driver_error = rq->req_stat;

        if (fs->driver_error == RS_SUCCESS)
                fs->status = ATTO_STS_SUCCESS;
        else
                fs->status = ATTO_STS_FAILED;
}

/* Prepare an FS IOCTL request to be sent to the firmware. */
bool esas2r_process_fs_ioctl(struct esas2r_adapter *a,
                             struct esas2r_ioctl_fs *fs,
                             struct esas2r_request *rq,
                             struct esas2r_sg_context *sgc)
{
        u8 cmdcnt = (u8)ARRAY_SIZE(cmd_to_fls_func);
        struct esas2r_ioctlfs_command *fsc = &fs->command;
        u8 func = 0;
        u32 datalen;

        fs->status = ATTO_STS_FAILED;
        fs->driver_error = RS_PENDING;

        if (fs->version > ESAS2R_FS_VER) {
                fs->status = ATTO_STS_INV_VERSION;
                return false;
        }

        if (fsc->command >= cmdcnt) {
                fs->status = ATTO_STS_INV_FUNC;
                return false;
        }

        func = cmd_to_fls_func[fsc->command];
        if (func == 0xFF) {
                fs->status = ATTO_STS_INV_FUNC;
                return false;
        }

        if (fsc->command != ESAS2R_FS_CMD_CANCEL) {
                if ((a->pcid->device != ATTO_DID_MV_88RC9580
                     || fs->adap_type != ESAS2R_FS_AT_ESASRAID2)
                    && (a->pcid->device != ATTO_DID_MV_88RC9580TS
                        || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2)
                    && (a->pcid->device != ATTO_DID_MV_88RC9580TSE
                        || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2E)
                    && (a->pcid->device != ATTO_DID_MV_88RC9580TL
                        || fs->adap_type != ESAS2R_FS_AT_TLSASHBA)) {
                        fs->status = ATTO_STS_INV_ADAPTER;
                        return false;
                }

                if (fs->driver_ver > ESAS2R_FS_DRVR_VER) {
                        fs->status = ATTO_STS_INV_DRVR_VER;
                        return false;
                }
        }

        if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
                fs->status = ATTO_STS_DEGRADED;
                return false;
        }

        rq->interrupt_cb = esas2r_complete_fs_ioctl;
        rq->interrupt_cx = fs;
        datalen = le32_to_cpu(fsc->length);
        esas2r_build_flash_req(a,
                               rq,
                               func,
                               fsc->checksum,
                               le32_to_cpu(fsc->flash_addr),
                               datalen);

        if (func == VDA_FLASH_WRITE
            || func == VDA_FLASH_READ) {
                if (datalen == 0) {
                        fs->status = ATTO_STS_INV_FUNC;
                        return false;
                }

                esas2r_sgc_init(sgc, a, rq, rq->vrq->flash.data.sge);
                sgc->length = datalen;

                if (!esas2r_build_sg_list(a, rq, sgc)) {
                        fs->status = ATTO_STS_OUT_OF_RSRC;
                        return false;
                }
        }

        if (func == VDA_FLASH_COMMIT)
                esas2r_disable_heartbeat(a);

        esas2r_start_request(a, rq);

        return true;
}

static bool esas2r_flash_access(struct esas2r_adapter *a, u32 function)
{
        u32 starttime;
        u32 timeout;
        u32 intstat;
        u32 doorbell;

        /* Disable chip interrupts awhile */
        if (function == DRBL_FLASH_REQ)
                esas2r_disable_chip_interrupts(a);

        /* Issue the request to the firmware */
        esas2r_write_register_dword(a, MU_DOORBELL_IN, function);

        /* Now wait for the firmware to process it */
        starttime = jiffies_to_msecs(jiffies);

        if (test_bit(AF_CHPRST_PENDING, &a->flags) ||
            test_bit(AF_DISC_PENDING, &a->flags))
                timeout = 40000;
        else
                timeout = 5000;

        while (true) {
                intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);

                if (intstat & MU_INTSTAT_DRBL) {
                        /* Got a doorbell interrupt.  Check for the function */
                        doorbell =
                                esas2r_read_register_dword(a, MU_DOORBELL_OUT);
                        esas2r_write_register_dword(a, MU_DOORBELL_OUT,
                                                    doorbell);
                        if (doorbell & function)
                                break;
                }

                schedule_timeout_interruptible(msecs_to_jiffies(100));

                if ((jiffies_to_msecs(jiffies) - starttime) > timeout) {
                        /*
                         * Iimeout.  If we were requesting flash access,
                         * indicate we are done so the firmware knows we gave
                         * up.  If this was a REQ, we also need to re-enable
                         * chip interrupts.
                         */
                        if (function == DRBL_FLASH_REQ) {
                                esas2r_hdebug("flash access timeout");
                                esas2r_write_register_dword(a, MU_DOORBELL_IN,
                                                            DRBL_FLASH_DONE);
                                esas2r_enable_chip_interrupts(a);
                        } else {
                                esas2r_hdebug("flash release timeout");
                        }

                        return false;
                }
        }

        /* if we're done, re-enable chip interrupts */
        if (function == DRBL_FLASH_DONE)
                esas2r_enable_chip_interrupts(a);

        return true;
}

#define WINDOW_SIZE ((signed int)MW_DATA_WINDOW_SIZE)

bool esas2r_read_flash_block(struct esas2r_adapter *a,
                             void *to,
                             u32 from,
                             u32 size)
{
        u8 *end = (u8 *)to;

        /* Try to acquire access to the flash */
        if (!esas2r_flash_access(a, DRBL_FLASH_REQ))
                return false;

        while (size) {
                u32 len;
                u32 offset;
                u32 iatvr;

                if (test_bit(AF2_SERIAL_FLASH, &a->flags2))
                        iatvr = MW_DATA_ADDR_SER_FLASH + (from & -WINDOW_SIZE);
                else
                        iatvr = MW_DATA_ADDR_PAR_FLASH + (from & -WINDOW_SIZE);

                esas2r_map_data_window(a, iatvr);
                offset = from & (WINDOW_SIZE - 1);
                len = size;

                if (len > WINDOW_SIZE - offset)
                        len = WINDOW_SIZE - offset;

                from += len;
                size -= len;

                while (len--) {
                        *end++ = esas2r_read_data_byte(a, offset);
                        offset++;
                }
        }

        /* Release flash access */
        esas2r_flash_access(a, DRBL_FLASH_DONE);
        return true;
}

bool esas2r_read_flash_rev(struct esas2r_adapter *a)
{
        u8 bytes[256];
        u16 *pw;
        u16 *pwstart;
        u16 type;
        u16 size;
        u32 sz;

        sz = sizeof(bytes);
        pw = (u16 *)(bytes + sz);
        pwstart = (u16 *)bytes + 2;

        if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_CPYR - sz, sz))
                goto invalid_rev;

        while (pw >= pwstart) {
                pw--;
                type = le16_to_cpu(*pw);
                pw--;
                size = le16_to_cpu(*pw);
                pw -= size / 2;

                if (type == FBT_CPYR
                    || type == FBT_SETUP
                    || pw < pwstart)
                        continue;

                if (type == FBT_FLASH_VER)
                        a->flash_ver = le32_to_cpu(*(u32 *)pw);

                break;
        }

invalid_rev:
        return esas2r_print_flash_rev(a);
}

bool esas2r_print_flash_rev(struct esas2r_adapter *a)
{
        u16 year = LOWORD(a->flash_ver);
        u8 day = LOBYTE(HIWORD(a->flash_ver));
        u8 month = HIBYTE(HIWORD(a->flash_ver));

        if (day == 0
            || month == 0
            || day > 31
            || month > 12
            || year < 2006
            || year > 9999) {
                strcpy(a->flash_rev, "not found");
                a->flash_ver = 0;
                return false;
        }

        sprintf(a->flash_rev, "%02d/%02d/%04d", month, day, year);
        esas2r_hdebug("flash version: %s", a->flash_rev);
        return true;
}

/*
 * Find the type of boot image type that is currently in the flash.
 * The chip only has a 64 KB PCI-e expansion ROM
 * size so only one image can be flashed at a time.
 */
bool esas2r_read_image_type(struct esas2r_adapter *a)
{
        u8 bytes[256];
        struct esas2r_boot_image *bi;
        struct esas2r_boot_header *bh;
        u32 sz;
        u32 len;
        u32 offset;

        /* Start at the base of the boot images and look for a valid image */
        sz = sizeof(bytes);
        len = FLS_LENGTH_BOOT;
        offset = 0;

        while (true) {
                if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_BOOT +
                                             offset,
                                             sz))
                        goto invalid_rev;

                bi = (struct esas2r_boot_image *)bytes;
                bh = (struct esas2r_boot_header *)((u8 *)bi +
                                                   le16_to_cpu(
                                                           bi->header_offset));
                if (bi->signature != cpu_to_le16(0xAA55))
                        goto invalid_rev;

                if (bh->code_type == CODE_TYPE_PC) {
                        strcpy(a->image_type, "BIOS");

                        return true;
                } else if (bh->code_type == CODE_TYPE_EFI) {
                        struct esas2r_efi_image *ei;

                        /*
                         * So we have an EFI image.  There are several types
                         * so see which architecture we have.
                         */
                        ei = (struct esas2r_efi_image *)bytes;

                        switch (le16_to_cpu(ei->machine_type)) {
                        case EFI_MACHINE_IA32:
                                strcpy(a->image_type, "EFI 32-bit");
                                return true;

                        case EFI_MACHINE_IA64:
                                strcpy(a->image_type, "EFI itanium");
                                return true;

                        case EFI_MACHINE_X64:
                                strcpy(a->image_type, "EFI 64-bit");
                                return true;

                        case EFI_MACHINE_EBC:
                                strcpy(a->image_type, "EFI EBC");
                                return true;

                        default:
                                goto invalid_rev;
                        }
                } else {
                        u32 thislen;

                        /* jump to the next image */
                        thislen = (u32)le16_to_cpu(bh->image_length) * 512;
                        if (thislen == 0
                            || thislen + offset > len
                            || bh->indicator == INDICATOR_LAST)
                                break;

                        offset += thislen;
                }
        }

invalid_rev:
        strcpy(a->image_type, "no boot images");
        return false;
}

/*
 *  Read and validate current NVRAM parameters by accessing
 *  physical NVRAM directly.  if currently stored parameters are
 *  invalid, use the defaults.
 */
bool esas2r_nvram_read_direct(struct esas2r_adapter *a)
{
        bool result;

        if (down_interruptible(&a->nvram_semaphore))
                return false;

        if (!esas2r_read_flash_block(a, a->nvram, FLS_OFFSET_NVR,
                                     sizeof(struct esas2r_sas_nvram))) {
                esas2r_hdebug("NVRAM read failed, using defaults");
                up(&a->nvram_semaphore);
                return false;
        }

        result = esas2r_nvram_validate(a);

        up(&a->nvram_semaphore);

        return result;
}

/* Interrupt callback to process NVRAM completions. */
static void esas2r_nvram_callback(struct esas2r_adapter *a,
                                  struct esas2r_request *rq)
{
        struct atto_vda_flash_req *vrq = &rq->vrq->flash;

        if (rq->req_stat == RS_SUCCESS) {
                /* last request was successful.  see what to do now. */

                switch (vrq->sub_func) {
                case VDA_FLASH_BEGINW:
                        vrq->sub_func = VDA_FLASH_WRITE;
                        rq->req_stat = RS_PENDING;
                        break;

                case VDA_FLASH_WRITE:
                        vrq->sub_func = VDA_FLASH_COMMIT;
                        rq->req_stat = RS_PENDING;
                        break;

                case VDA_FLASH_READ:
                        esas2r_nvram_validate(a);
                        break;

                case VDA_FLASH_COMMIT:
                default:
                        break;
                }
        }

        if (rq->req_stat != RS_PENDING) {
                /* update the NVRAM state */
                if (rq->req_stat == RS_SUCCESS)
                        set_bit(AF_NVR_VALID, &a->flags);
                else
                        clear_bit(AF_NVR_VALID, &a->flags);

                esas2r_enable_heartbeat(a);

                up(&a->nvram_semaphore);
        }
}

/*
 * Write the contents of nvram to the adapter's physical NVRAM.
 * The cached copy of the NVRAM is also updated.
 */
bool esas2r_nvram_write(struct esas2r_adapter *a, struct esas2r_request *rq,
                        struct esas2r_sas_nvram *nvram)
{
        struct esas2r_sas_nvram *n = nvram;
        u8 sas_address_bytes[8];
        u32 *sas_address_dwords = (u32 *)&sas_address_bytes[0];
        struct atto_vda_flash_req *vrq = &rq->vrq->flash;

        if (test_bit(AF_DEGRADED_MODE, &a->flags))
                return false;

        if (down_interruptible(&a->nvram_semaphore))
                return false;

        if (n == NULL)
                n = a->nvram;

        /* check the validity of the settings */
        if (n->version > SASNVR_VERSION) {
                up(&a->nvram_semaphore);
                return false;
        }

        memcpy(&sas_address_bytes[0], n->sas_addr, 8);

        if (sas_address_bytes[0] != 0x50
            || sas_address_bytes[1] != 0x01
            || sas_address_bytes[2] != 0x08
            || (sas_address_bytes[3] & 0xF0) != 0x60
            || ((sas_address_bytes[3] & 0x0F) | sas_address_dwords[1]) == 0) {
                up(&a->nvram_semaphore);
                return false;
        }

        if (n->spin_up_delay > SASNVR_SPINUP_MAX)
                n->spin_up_delay = SASNVR_SPINUP_MAX;

        n->version = SASNVR_VERSION;
        n->checksum = n->checksum - esas2r_nvramcalc_cksum(n);
        memcpy(a->nvram, n, sizeof(struct esas2r_sas_nvram));

        /* write the NVRAM */
        n = a->nvram;
        esas2r_disable_heartbeat(a);

        esas2r_build_flash_req(a,
                               rq,
                               VDA_FLASH_BEGINW,
                               esas2r_nvramcalc_xor_cksum(n),
                               FLS_OFFSET_NVR,
                               sizeof(struct esas2r_sas_nvram));

        if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {

                vrq->data.sge[0].length =
                        cpu_to_le32(SGE_LAST |
                                    sizeof(struct esas2r_sas_nvram));
                vrq->data.sge[0].address = cpu_to_le64(
                        a->uncached_phys + (u64)((u8 *)n - a->uncached));
        } else {
                vrq->data.prde[0].ctl_len =
                        cpu_to_le32(sizeof(struct esas2r_sas_nvram));
                vrq->data.prde[0].address = cpu_to_le64(
                        a->uncached_phys
                        + (u64)((u8 *)n - a->uncached));
        }
        rq->interrupt_cb = esas2r_nvram_callback;
        esas2r_start_request(a, rq);
        return true;
}

/* Validate the cached NVRAM.  if the NVRAM is invalid, load the defaults. */
bool esas2r_nvram_validate(struct esas2r_adapter *a)
{
        struct esas2r_sas_nvram *n = a->nvram;
        bool rslt = false;

        if (n->signature[0] != 'E'
            || n->signature[1] != 'S'
            || n->signature[2] != 'A'
            || n->signature[3] != 'S') {
                esas2r_hdebug("invalid NVRAM signature");
        } else if (esas2r_nvramcalc_cksum(n)) {
                esas2r_hdebug("invalid NVRAM checksum");
        } else if (n->version > SASNVR_VERSION) {
                esas2r_hdebug("invalid NVRAM version");
        } else {
                set_bit(AF_NVR_VALID, &a->flags);
                rslt = true;
        }

        if (rslt == false) {
                esas2r_hdebug("using defaults");
                esas2r_nvram_set_defaults(a);
        }

        return rslt;
}

/*
 * Set the cached NVRAM to defaults.  note that this function sets the default
 * NVRAM when it has been determined that the physical NVRAM is invalid.
 * In this case, the SAS address is fabricated.
 */
void esas2r_nvram_set_defaults(struct esas2r_adapter *a)
{
        struct esas2r_sas_nvram *n = a->nvram;
        u32 time = jiffies_to_msecs(jiffies);

        clear_bit(AF_NVR_VALID, &a->flags);
        *n = default_sas_nvram;
        n->sas_addr[3] |= 0x0F;
        n->sas_addr[4] = HIBYTE(LOWORD(time));
        n->sas_addr[5] = LOBYTE(LOWORD(time));
        n->sas_addr[6] = a->pcid->bus->number;
        n->sas_addr[7] = a->pcid->devfn;
}

void esas2r_nvram_get_defaults(struct esas2r_adapter *a,
                               struct esas2r_sas_nvram *nvram)
{
        u8 sas_addr[8];

        /*
         * in case we are copying the defaults into the adapter, copy the SAS
         * address out first.
         */
        memcpy(&sas_addr[0], a->nvram->sas_addr, 8);
        *nvram = default_sas_nvram;
        memcpy(&nvram->sas_addr[0], &sas_addr[0], 8);
}

bool esas2r_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi,
                   struct esas2r_request *rq, struct esas2r_sg_context *sgc)
{
        struct esas2r_flash_context *fc = &a->flash_context;
        u8 j;
        struct esas2r_component_header *ch;

        if (test_and_set_bit(AF_FLASH_LOCK, &a->flags)) {
                /* flag was already set */
                fi->status = FI_STAT_BUSY;
                return false;
        }

        memcpy(&fc->sgc, sgc, sizeof(struct esas2r_sg_context));
        sgc = &fc->sgc;
        fc->fi = fi;
        fc->sgc_offset = sgc->cur_offset;
        rq->req_stat = RS_SUCCESS;
        rq->interrupt_cx = fc;

        switch (fi->fi_version) {
        case FI_VERSION_1:
                fc->scratch = ((struct esas2r_flash_img *)fi)->scratch_buf;
                fc->num_comps = FI_NUM_COMPS_V1;
                fc->fi_hdr_len = sizeof(struct esas2r_flash_img);
                break;

        default:
                return complete_fmapi_req(a, rq, FI_STAT_IMG_VER);
        }

        if (test_bit(AF_DEGRADED_MODE, &a->flags))
                return complete_fmapi_req(a, rq, FI_STAT_DEGRADED);

        switch (fi->action) {
        case FI_ACT_DOWN: /* Download the components */
                /* Verify the format of the flash image */
                if (!verify_fi(a, fc))
                        return complete_fmapi_req(a, rq, fi->status);

                /* Adjust the BIOS fields that are dependent on the HBA */
                ch = &fi->cmp_hdr[CH_IT_BIOS];

                if (ch->length)
                        fix_bios(a, fi);

                /* Adjust the EFI fields that are dependent on the HBA */
                ch = &fi->cmp_hdr[CH_IT_EFI];

                if (ch->length)
                        fix_efi(a, fi);

                /*
                 * Since the image was just modified, compute the checksum on
                 * the modified image.  First update the CRC for the composite
                 * expansion ROM image.
                 */
                fi->checksum = calc_fi_checksum(fc);

                /* Disable the heartbeat */
                esas2r_disable_heartbeat(a);

                /* Now start up the download sequence */
                fc->task = FMTSK_ERASE_BOOT;
                fc->func = VDA_FLASH_BEGINW;
                fc->comp_typ = CH_IT_CFG;
                fc->flsh_addr = FLS_OFFSET_BOOT;
                fc->sgc.length = FLS_LENGTH_BOOT;
                fc->sgc.cur_offset = NULL;

                /* Setup the callback address */
                fc->interrupt_cb = fw_download_proc;
                break;

        case FI_ACT_UPSZ: /* Get upload sizes */
                fi->adap_typ = get_fi_adap_type(a);
                fi->flags = 0;
                fi->num_comps = fc->num_comps;
                fi->length = fc->fi_hdr_len;

                /* Report the type of boot image in the rel_version string */
                memcpy(fi->rel_version, a->image_type,
                       sizeof(fi->rel_version));

                /* Build the component headers */
                for (j = 0, ch = fi->cmp_hdr;
                     j < fi->num_comps;
                     j++, ch++) {
                        ch->img_type = j;
                        ch->status = CH_STAT_PENDING;
                        ch->length = 0;
                        ch->version = 0xffffffff;
                        ch->image_offset = 0;
                        ch->pad[0] = 0;
                        ch->pad[1] = 0;
                }

                if (a->flash_ver != 0) {
                        fi->cmp_hdr[CH_IT_BIOS].version =
                                fi->cmp_hdr[CH_IT_MAC].version =
                                        fi->cmp_hdr[CH_IT_EFI].version =
                                                fi->cmp_hdr[CH_IT_CFG].version
                                                        = a->flash_ver;

                        fi->cmp_hdr[CH_IT_BIOS].status =
                                fi->cmp_hdr[CH_IT_MAC].status =
                                        fi->cmp_hdr[CH_IT_EFI].status =
                                                fi->cmp_hdr[CH_IT_CFG].status =
                                                        CH_STAT_SUCCESS;

                        return complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
                }

                fallthrough;

        case FI_ACT_UP: /* Upload the components */
        default:
                return complete_fmapi_req(a, rq, FI_STAT_INVALID);
        }

        /*
         * If we make it here, fc has been setup to do the first task.  Call
         * load_image to format the request, start it, and get out.  The
         * interrupt code will call the callback when the first message is
         * complete.
         */
        if (!load_image(a, rq))
                return complete_fmapi_req(a, rq, FI_STAT_FAILED);

        esas2r_start_request(a, rq);

        return true;
}