| |
| /* |
| * 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 suppported 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); |
| } |
| |
| /* fall through */ |
| |
| 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; |
| } |