|  |  | 
|  | /* | 
|  | *  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; | 
|  | } |