/* * 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.
*/
while (len--)
cksum = cksum + p[len]; return cksum;
}
/* Interrupt callback to process FM API write requests. */ staticvoid 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;
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.
*/ staticvoid 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;
/* * 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]);
/* 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 */ staticbool 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. */ staticvoid 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;
/* Process each phase of the flash download process. */ staticvoid 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;
/* * 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;
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);
/* 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;
/* 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;
/* * 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
*/ staticbool 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; returnfalse;
}
/* 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; returnfalse;
}
/* * 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; returnfalse;
}
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; returnfalse;
}
/* Compare fi->length to the sum of ch->length fields */ if (len != fi->length - fc->fi_hdr_len) {
fi->status = FI_STAT_LENGTH; returnfalse;
}
/* Compute the checksum - it should come out zero */ if (fi->checksum != calc_fi_checksum(fc)) {
fi->status = FI_STAT_CHKSUM; returnfalse;
}
returntrue;
}
/* Fill in the FS IOCTL response data from a completed request. */ staticvoid 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);
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;
}
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");
}
returnfalse;
}
}
/* if we're done, re-enable chip interrupts */ if (function == DRBL_FLASH_DONE)
esas2r_enable_chip_interrupts(a);
/* * 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");
/* 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"); returnfalse;
}
/* * 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)) returnfalse;
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); returnfalse;
}
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)) returnfalse;
if (down_interruptible(&a->nvram_semaphore)) returnfalse;
if (n == NULL)
n = a->nvram;
/* check the validity of the settings */ if (n->version > SASNVR_VERSION) {
up(&a->nvram_semaphore); returnfalse;
}
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);
/* * 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);
}
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));
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);
returntrue;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.