Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Bradley Grove | 6227 | 99.81% | 4 | 50.00% |
Dan Carpenter | 8 | 0.13% | 1 | 12.50% |
Gustavo A. R. Silva | 2 | 0.03% | 1 | 12.50% |
Boris Bodemann | 1 | 0.02% | 1 | 12.50% |
Julia Lawall | 1 | 0.02% | 1 | 12.50% |
Total | 6239 | 8 |
/* * 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; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1