diff options
Diffstat (limited to 'drivers/scsi/esas2r/esas2r_flash.c')
| -rw-r--r-- | drivers/scsi/esas2r/esas2r_flash.c | 1521 | 
1 files changed, 1521 insertions, 0 deletions
diff --git a/drivers/scsi/esas2r/esas2r_flash.c b/drivers/scsi/esas2r/esas2r_flash.c new file mode 100644 index 00000000000..b7dc59fca7a --- /dev/null +++ b/drivers/scsi/esas2r/esas2r_flash.c @@ -0,0 +1,1521 @@ + +/* + *  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)  valdiate each component header. check the img_type and + *                  length fields + *              5)  valdiate 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"); +		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; +}  | 
