mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1522 lines
38 KiB
1522 lines
38 KiB
|
|
/* |
|
* 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:[email protected]) |
|
* |
|
* This program is free software; you can redistribute it and/or |
|
* modify it under the terms of the GNU General Public License |
|
* as published by the Free Software Foundation; either version 2 |
|
* of the License, or (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* NO WARRANTY |
|
* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR |
|
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT |
|
* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, |
|
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is |
|
* solely responsible for determining the appropriateness of using and |
|
* distributing the Program and assumes all risks associated with its |
|
* exercise of rights under this Agreement, including but not limited to |
|
* the risks and costs of program errors, damage to or loss of data, |
|
* programs or equipment, and unavailability or interruption of operations. |
|
* |
|
* DISCLAIMER OF LIABILITY |
|
* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY |
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND |
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
|
* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED |
|
* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
|
* USA. |
|
*/ |
|
|
|
#include "esas2r.h" |
|
|
|
/* local macro defs */ |
|
#define esas2r_nvramcalc_cksum(n) \ |
|
(esas2r_calc_byte_cksum((u8 *)(n), sizeof(struct esas2r_sas_nvram), \ |
|
SASNVR_CKSUM_SEED)) |
|
#define esas2r_nvramcalc_xor_cksum(n) \ |
|
(esas2r_calc_byte_xor_cksum((u8 *)(n), \ |
|
sizeof(struct esas2r_sas_nvram), 0)) |
|
|
|
#define ESAS2R_FS_DRVR_VER 2 |
|
|
|
static struct esas2r_sas_nvram default_sas_nvram = { |
|
{ 'E', 'S', 'A', 'S' }, /* signature */ |
|
SASNVR_VERSION, /* version */ |
|
0, /* checksum */ |
|
31, /* max_lun_for_target */ |
|
SASNVR_PCILAT_MAX, /* pci_latency */ |
|
SASNVR1_BOOT_DRVR, /* options1 */ |
|
SASNVR2_HEARTBEAT | SASNVR2_SINGLE_BUS /* options2 */ |
|
| SASNVR2_SW_MUX_CTRL, |
|
SASNVR_COAL_DIS, /* int_coalescing */ |
|
SASNVR_CMDTHR_NONE, /* cmd_throttle */ |
|
3, /* dev_wait_time */ |
|
1, /* dev_wait_count */ |
|
0, /* spin_up_delay */ |
|
0, /* ssp_align_rate */ |
|
{ 0x50, 0x01, 0x08, 0x60, /* sas_addr */ |
|
0x00, 0x00, 0x00, 0x00 }, |
|
{ SASNVR_SPEED_AUTO }, /* phy_speed */ |
|
{ SASNVR_MUX_DISABLED }, /* SAS multiplexing */ |
|
{ 0 }, /* phy_flags */ |
|
SASNVR_SORT_SAS_ADDR, /* sort_type */ |
|
3, /* dpm_reqcmd_lmt */ |
|
3, /* dpm_stndby_time */ |
|
0, /* dpm_active_time */ |
|
{ 0 }, /* phy_target_id */ |
|
SASNVR_VSMH_DISABLED, /* virt_ses_mode */ |
|
SASNVR_RWM_DEFAULT, /* read_write_mode */ |
|
0, /* link down timeout */ |
|
{ 0 } /* reserved */ |
|
}; |
|
|
|
static u8 cmd_to_fls_func[] = { |
|
0xFF, |
|
VDA_FLASH_READ, |
|
VDA_FLASH_BEGINW, |
|
VDA_FLASH_WRITE, |
|
VDA_FLASH_COMMIT, |
|
VDA_FLASH_CANCEL |
|
}; |
|
|
|
static u8 esas2r_calc_byte_xor_cksum(u8 *addr, u32 len, u8 seed) |
|
{ |
|
u32 cksum = seed; |
|
u8 *p = (u8 *)&cksum; |
|
|
|
while (len) { |
|
if (((uintptr_t)addr & 3) == 0) |
|
break; |
|
|
|
cksum = cksum ^ *addr; |
|
addr++; |
|
len--; |
|
} |
|
while (len >= sizeof(u32)) { |
|
cksum = cksum ^ *(u32 *)addr; |
|
addr += 4; |
|
len -= 4; |
|
} |
|
while (len--) { |
|
cksum = cksum ^ *addr; |
|
addr++; |
|
} |
|
return p[0] ^ p[1] ^ p[2] ^ p[3]; |
|
} |
|
|
|
static u8 esas2r_calc_byte_cksum(void *addr, u32 len, u8 seed) |
|
{ |
|
u8 *p = (u8 *)addr; |
|
u8 cksum = seed; |
|
|
|
while (len--) |
|
cksum = cksum + p[len]; |
|
return cksum; |
|
} |
|
|
|
/* Interrupt callback to process FM API write requests. */ |
|
static void esas2r_fmapi_callback(struct esas2r_adapter *a, |
|
struct esas2r_request *rq) |
|
{ |
|
struct atto_vda_flash_req *vrq = &rq->vrq->flash; |
|
struct esas2r_flash_context *fc = |
|
(struct esas2r_flash_context *)rq->interrupt_cx; |
|
|
|
if (rq->req_stat == RS_SUCCESS) { |
|
/* Last request was successful. See what to do now. */ |
|
switch (vrq->sub_func) { |
|
case VDA_FLASH_BEGINW: |
|
if (fc->sgc.cur_offset == NULL) |
|
goto commit; |
|
|
|
vrq->sub_func = VDA_FLASH_WRITE; |
|
rq->req_stat = RS_PENDING; |
|
break; |
|
|
|
case VDA_FLASH_WRITE: |
|
commit: |
|
vrq->sub_func = VDA_FLASH_COMMIT; |
|
rq->req_stat = RS_PENDING; |
|
rq->interrupt_cb = fc->interrupt_cb; |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
} |
|
|
|
if (rq->req_stat != RS_PENDING) |
|
/* |
|
* All done. call the real callback to complete the FM API |
|
* request. We should only get here if a BEGINW or WRITE |
|
* operation failed. |
|
*/ |
|
(*fc->interrupt_cb)(a, rq); |
|
} |
|
|
|
/* |
|
* Build a flash request based on the flash context. The request status |
|
* is filled in on an error. |
|
*/ |
|
static void build_flash_msg(struct esas2r_adapter *a, |
|
struct esas2r_request *rq) |
|
{ |
|
struct esas2r_flash_context *fc = |
|
(struct esas2r_flash_context *)rq->interrupt_cx; |
|
struct esas2r_sg_context *sgc = &fc->sgc; |
|
u8 cksum = 0; |
|
|
|
/* calculate the checksum */ |
|
if (fc->func == VDA_FLASH_BEGINW) { |
|
if (sgc->cur_offset) |
|
cksum = esas2r_calc_byte_xor_cksum(sgc->cur_offset, |
|
sgc->length, |
|
0); |
|
rq->interrupt_cb = esas2r_fmapi_callback; |
|
} else { |
|
rq->interrupt_cb = fc->interrupt_cb; |
|
} |
|
esas2r_build_flash_req(a, |
|
rq, |
|
fc->func, |
|
cksum, |
|
fc->flsh_addr, |
|
sgc->length); |
|
|
|
esas2r_rq_free_sg_lists(rq, a); |
|
|
|
/* |
|
* remember the length we asked for. we have to keep track of |
|
* the current amount done so we know how much to compare when |
|
* doing the verification phase. |
|
*/ |
|
fc->curr_len = fc->sgc.length; |
|
|
|
if (sgc->cur_offset) { |
|
/* setup the S/G context to build the S/G table */ |
|
esas2r_sgc_init(sgc, a, rq, &rq->vrq->flash.data.sge[0]); |
|
|
|
if (!esas2r_build_sg_list(a, rq, sgc)) { |
|
rq->req_stat = RS_BUSY; |
|
return; |
|
} |
|
} else { |
|
fc->sgc.length = 0; |
|
} |
|
|
|
/* update the flsh_addr to the next one to write to */ |
|
fc->flsh_addr += fc->curr_len; |
|
} |
|
|
|
/* determine the method to process the flash request */ |
|
static bool load_image(struct esas2r_adapter *a, struct esas2r_request *rq) |
|
{ |
|
/* |
|
* assume we have more to do. if we return with the status set to |
|
* RS_PENDING, FM API tasks will continue. |
|
*/ |
|
rq->req_stat = RS_PENDING; |
|
if (test_bit(AF_DEGRADED_MODE, &a->flags)) |
|
/* not suppported for now */; |
|
else |
|
build_flash_msg(a, rq); |
|
|
|
return rq->req_stat == RS_PENDING; |
|
} |
|
|
|
/* boot image fixer uppers called before downloading the image. */ |
|
static void fix_bios(struct esas2r_adapter *a, struct esas2r_flash_img *fi) |
|
{ |
|
struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_BIOS]; |
|
struct esas2r_pc_image *pi; |
|
struct esas2r_boot_header *bh; |
|
|
|
pi = (struct esas2r_pc_image *)((u8 *)fi + ch->image_offset); |
|
bh = |
|
(struct esas2r_boot_header *)((u8 *)pi + |
|
le16_to_cpu(pi->header_offset)); |
|
bh->device_id = cpu_to_le16(a->pcid->device); |
|
|
|
/* Recalculate the checksum in the PNP header if there */ |
|
if (pi->pnp_offset) { |
|
u8 *pnp_header_bytes = |
|
((u8 *)pi + le16_to_cpu(pi->pnp_offset)); |
|
|
|
/* Identifier - dword that starts at byte 10 */ |
|
*((u32 *)&pnp_header_bytes[10]) = |
|
cpu_to_le32(MAKEDWORD(a->pcid->subsystem_vendor, |
|
a->pcid->subsystem_device)); |
|
|
|
/* Checksum - byte 9 */ |
|
pnp_header_bytes[9] -= esas2r_calc_byte_cksum(pnp_header_bytes, |
|
32, 0); |
|
} |
|
|
|
/* Recalculate the checksum needed by the PC */ |
|
pi->checksum = pi->checksum - |
|
esas2r_calc_byte_cksum((u8 *)pi, ch->length, 0); |
|
} |
|
|
|
static void fix_efi(struct esas2r_adapter *a, struct esas2r_flash_img *fi) |
|
{ |
|
struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_EFI]; |
|
u32 len = ch->length; |
|
u32 offset = ch->image_offset; |
|
struct esas2r_efi_image *ei; |
|
struct esas2r_boot_header *bh; |
|
|
|
while (len) { |
|
u32 thislen; |
|
|
|
ei = (struct esas2r_efi_image *)((u8 *)fi + offset); |
|
bh = (struct esas2r_boot_header *)((u8 *)ei + |
|
le16_to_cpu( |
|
ei->header_offset)); |
|
bh->device_id = cpu_to_le16(a->pcid->device); |
|
thislen = (u32)le16_to_cpu(bh->image_length) * 512; |
|
|
|
if (thislen > len) |
|
break; |
|
|
|
len -= thislen; |
|
offset += thislen; |
|
} |
|
} |
|
|
|
/* Complete a FM API request with the specified status. */ |
|
static bool complete_fmapi_req(struct esas2r_adapter *a, |
|
struct esas2r_request *rq, u8 fi_stat) |
|
{ |
|
struct esas2r_flash_context *fc = |
|
(struct esas2r_flash_context *)rq->interrupt_cx; |
|
struct esas2r_flash_img *fi = fc->fi; |
|
|
|
fi->status = fi_stat; |
|
fi->driver_error = rq->req_stat; |
|
rq->interrupt_cb = NULL; |
|
rq->req_stat = RS_SUCCESS; |
|
|
|
if (fi_stat != FI_STAT_IMG_VER) |
|
memset(fc->scratch, 0, FM_BUF_SZ); |
|
|
|
esas2r_enable_heartbeat(a); |
|
clear_bit(AF_FLASH_LOCK, &a->flags); |
|
return false; |
|
} |
|
|
|
/* Process each phase of the flash download process. */ |
|
static void fw_download_proc(struct esas2r_adapter *a, |
|
struct esas2r_request *rq) |
|
{ |
|
struct esas2r_flash_context *fc = |
|
(struct esas2r_flash_context *)rq->interrupt_cx; |
|
struct esas2r_flash_img *fi = fc->fi; |
|
struct esas2r_component_header *ch; |
|
u32 len; |
|
u8 *p, *q; |
|
|
|
/* If the previous operation failed, just return. */ |
|
if (rq->req_stat != RS_SUCCESS) |
|
goto error; |
|
|
|
/* |
|
* If an upload just completed and the compare length is non-zero, |
|
* then we just read back part of the image we just wrote. verify the |
|
* section and continue reading until the entire image is verified. |
|
*/ |
|
if (fc->func == VDA_FLASH_READ |
|
&& fc->cmp_len) { |
|
ch = &fi->cmp_hdr[fc->comp_typ]; |
|
|
|
p = fc->scratch; |
|
q = (u8 *)fi /* start of the whole gob */ |
|
+ ch->image_offset /* start of the current image */ |
|
+ ch->length /* end of the current image */ |
|
- fc->cmp_len; /* where we are now */ |
|
|
|
/* |
|
* NOTE - curr_len is the exact count of bytes for the read |
|
* even when the end is read and its not a full buffer |
|
*/ |
|
for (len = fc->curr_len; len; len--) |
|
if (*p++ != *q++) |
|
goto error; |
|
|
|
fc->cmp_len -= fc->curr_len; /* # left to compare */ |
|
|
|
/* Update fc and determine the length for the next upload */ |
|
if (fc->cmp_len > FM_BUF_SZ) |
|
fc->sgc.length = FM_BUF_SZ; |
|
else |
|
fc->sgc.length = fc->cmp_len; |
|
|
|
fc->sgc.cur_offset = fc->sgc_offset + |
|
((u8 *)fc->scratch - (u8 *)fi); |
|
} |
|
|
|
/* |
|
* This code uses a 'while' statement since the next component may |
|
* have a length = zero. This can happen since some components are |
|
* not required. At the end of this 'while' we set up the length |
|
* for the next request and therefore sgc.length can be = 0. |
|
*/ |
|
while (fc->sgc.length == 0) { |
|
ch = &fi->cmp_hdr[fc->comp_typ]; |
|
|
|
switch (fc->task) { |
|
case FMTSK_ERASE_BOOT: |
|
/* the BIOS image is written next */ |
|
ch = &fi->cmp_hdr[CH_IT_BIOS]; |
|
if (ch->length == 0) |
|
goto no_bios; |
|
|
|
fc->task = FMTSK_WRTBIOS; |
|
fc->func = VDA_FLASH_BEGINW; |
|
fc->comp_typ = CH_IT_BIOS; |
|
fc->flsh_addr = FLS_OFFSET_BOOT; |
|
fc->sgc.length = ch->length; |
|
fc->sgc.cur_offset = fc->sgc_offset + |
|
ch->image_offset; |
|
break; |
|
|
|
case FMTSK_WRTBIOS: |
|
/* |
|
* The BIOS image has been written - read it and |
|
* verify it |
|
*/ |
|
fc->task = FMTSK_READBIOS; |
|
fc->func = VDA_FLASH_READ; |
|
fc->flsh_addr = FLS_OFFSET_BOOT; |
|
fc->cmp_len = ch->length; |
|
fc->sgc.length = FM_BUF_SZ; |
|
fc->sgc.cur_offset = fc->sgc_offset |
|
+ ((u8 *)fc->scratch - |
|
(u8 *)fi); |
|
break; |
|
|
|
case FMTSK_READBIOS: |
|
no_bios: |
|
/* |
|
* Mark the component header status for the image |
|
* completed |
|
*/ |
|
ch->status = CH_STAT_SUCCESS; |
|
|
|
/* The MAC image is written next */ |
|
ch = &fi->cmp_hdr[CH_IT_MAC]; |
|
if (ch->length == 0) |
|
goto no_mac; |
|
|
|
fc->task = FMTSK_WRTMAC; |
|
fc->func = VDA_FLASH_BEGINW; |
|
fc->comp_typ = CH_IT_MAC; |
|
fc->flsh_addr = FLS_OFFSET_BOOT |
|
+ fi->cmp_hdr[CH_IT_BIOS].length; |
|
fc->sgc.length = ch->length; |
|
fc->sgc.cur_offset = fc->sgc_offset + |
|
ch->image_offset; |
|
break; |
|
|
|
case FMTSK_WRTMAC: |
|
/* The MAC image has been written - read and verify */ |
|
fc->task = FMTSK_READMAC; |
|
fc->func = VDA_FLASH_READ; |
|
fc->flsh_addr -= ch->length; |
|
fc->cmp_len = ch->length; |
|
fc->sgc.length = FM_BUF_SZ; |
|
fc->sgc.cur_offset = fc->sgc_offset |
|
+ ((u8 *)fc->scratch - |
|
(u8 *)fi); |
|
break; |
|
|
|
case FMTSK_READMAC: |
|
no_mac: |
|
/* |
|
* Mark the component header status for the image |
|
* completed |
|
*/ |
|
ch->status = CH_STAT_SUCCESS; |
|
|
|
/* The EFI image is written next */ |
|
ch = &fi->cmp_hdr[CH_IT_EFI]; |
|
if (ch->length == 0) |
|
goto no_efi; |
|
|
|
fc->task = FMTSK_WRTEFI; |
|
fc->func = VDA_FLASH_BEGINW; |
|
fc->comp_typ = CH_IT_EFI; |
|
fc->flsh_addr = FLS_OFFSET_BOOT |
|
+ fi->cmp_hdr[CH_IT_BIOS].length |
|
+ fi->cmp_hdr[CH_IT_MAC].length; |
|
fc->sgc.length = ch->length; |
|
fc->sgc.cur_offset = fc->sgc_offset + |
|
ch->image_offset; |
|
break; |
|
|
|
case FMTSK_WRTEFI: |
|
/* The EFI image has been written - read and verify */ |
|
fc->task = FMTSK_READEFI; |
|
fc->func = VDA_FLASH_READ; |
|
fc->flsh_addr -= ch->length; |
|
fc->cmp_len = ch->length; |
|
fc->sgc.length = FM_BUF_SZ; |
|
fc->sgc.cur_offset = fc->sgc_offset |
|
+ ((u8 *)fc->scratch - |
|
(u8 *)fi); |
|
break; |
|
|
|
case FMTSK_READEFI: |
|
no_efi: |
|
/* |
|
* Mark the component header status for the image |
|
* completed |
|
*/ |
|
ch->status = CH_STAT_SUCCESS; |
|
|
|
/* The CFG image is written next */ |
|
ch = &fi->cmp_hdr[CH_IT_CFG]; |
|
|
|
if (ch->length == 0) |
|
goto no_cfg; |
|
fc->task = FMTSK_WRTCFG; |
|
fc->func = VDA_FLASH_BEGINW; |
|
fc->comp_typ = CH_IT_CFG; |
|
fc->flsh_addr = FLS_OFFSET_CPYR - ch->length; |
|
fc->sgc.length = ch->length; |
|
fc->sgc.cur_offset = fc->sgc_offset + |
|
ch->image_offset; |
|
break; |
|
|
|
case FMTSK_WRTCFG: |
|
/* The CFG image has been written - read and verify */ |
|
fc->task = FMTSK_READCFG; |
|
fc->func = VDA_FLASH_READ; |
|
fc->flsh_addr = FLS_OFFSET_CPYR - ch->length; |
|
fc->cmp_len = ch->length; |
|
fc->sgc.length = FM_BUF_SZ; |
|
fc->sgc.cur_offset = fc->sgc_offset |
|
+ ((u8 *)fc->scratch - |
|
(u8 *)fi); |
|
break; |
|
|
|
case FMTSK_READCFG: |
|
no_cfg: |
|
/* |
|
* Mark the component header status for the image |
|
* completed |
|
*/ |
|
ch->status = CH_STAT_SUCCESS; |
|
|
|
/* |
|
* The download is complete. If in degraded mode, |
|
* attempt a chip reset. |
|
*/ |
|
if (test_bit(AF_DEGRADED_MODE, &a->flags)) |
|
esas2r_local_reset_adapter(a); |
|
|
|
a->flash_ver = fi->cmp_hdr[CH_IT_BIOS].version; |
|
esas2r_print_flash_rev(a); |
|
|
|
/* Update the type of boot image on the card */ |
|
memcpy(a->image_type, fi->rel_version, |
|
sizeof(fi->rel_version)); |
|
complete_fmapi_req(a, rq, FI_STAT_SUCCESS); |
|
return; |
|
} |
|
|
|
/* If verifying, don't try reading more than what's there */ |
|
if (fc->func == VDA_FLASH_READ |
|
&& fc->sgc.length > fc->cmp_len) |
|
fc->sgc.length = fc->cmp_len; |
|
} |
|
|
|
/* Build the request to perform the next action */ |
|
if (!load_image(a, rq)) { |
|
error: |
|
if (fc->comp_typ < fi->num_comps) { |
|
ch = &fi->cmp_hdr[fc->comp_typ]; |
|
ch->status = CH_STAT_FAILED; |
|
} |
|
|
|
complete_fmapi_req(a, rq, FI_STAT_FAILED); |
|
} |
|
} |
|
|
|
/* Determine the flash image adaptyp for this adapter */ |
|
static u8 get_fi_adap_type(struct esas2r_adapter *a) |
|
{ |
|
u8 type; |
|
|
|
/* use the device ID to get the correct adap_typ for this HBA */ |
|
switch (a->pcid->device) { |
|
case ATTO_DID_INTEL_IOP348: |
|
type = FI_AT_SUN_LAKE; |
|
break; |
|
|
|
case ATTO_DID_MV_88RC9580: |
|
case ATTO_DID_MV_88RC9580TS: |
|
case ATTO_DID_MV_88RC9580TSE: |
|
case ATTO_DID_MV_88RC9580TL: |
|
type = FI_AT_MV_9580; |
|
break; |
|
|
|
default: |
|
type = FI_AT_UNKNWN; |
|
break; |
|
} |
|
|
|
return type; |
|
} |
|
|
|
/* Size of config + copyright + flash_ver images, 0 for failure. */ |
|
static u32 chk_cfg(u8 *cfg, u32 length, u32 *flash_ver) |
|
{ |
|
u16 *pw = (u16 *)cfg - 1; |
|
u32 sz = 0; |
|
u32 len = length; |
|
|
|
if (len == 0) |
|
len = FM_BUF_SZ; |
|
|
|
if (flash_ver) |
|
*flash_ver = 0; |
|
|
|
while (true) { |
|
u16 type; |
|
u16 size; |
|
|
|
type = le16_to_cpu(*pw--); |
|
size = le16_to_cpu(*pw--); |
|
|
|
if (type != FBT_CPYR |
|
&& type != FBT_SETUP |
|
&& type != FBT_FLASH_VER) |
|
break; |
|
|
|
if (type == FBT_FLASH_VER |
|
&& flash_ver) |
|
*flash_ver = le32_to_cpu(*(u32 *)(pw - 1)); |
|
|
|
sz += size + (2 * sizeof(u16)); |
|
pw -= size / sizeof(u16); |
|
|
|
if (sz > len - (2 * sizeof(u16))) |
|
break; |
|
} |
|
|
|
/* See if we are comparing the size to the specified length */ |
|
if (length && sz != length) |
|
return 0; |
|
|
|
return sz; |
|
} |
|
|
|
/* Verify that the boot image is valid */ |
|
static u8 chk_boot(u8 *boot_img, u32 length) |
|
{ |
|
struct esas2r_boot_image *bi = (struct esas2r_boot_image *)boot_img; |
|
u16 hdroffset = le16_to_cpu(bi->header_offset); |
|
struct esas2r_boot_header *bh; |
|
|
|
if (bi->signature != le16_to_cpu(0xaa55) |
|
|| (long)hdroffset > |
|
(long)(65536L - sizeof(struct esas2r_boot_header)) |
|
|| (hdroffset & 3) |
|
|| (hdroffset < sizeof(struct esas2r_boot_image)) |
|
|| ((u32)hdroffset + sizeof(struct esas2r_boot_header) > length)) |
|
return 0xff; |
|
|
|
bh = (struct esas2r_boot_header *)((char *)bi + hdroffset); |
|
|
|
if (bh->signature[0] != 'P' |
|
|| bh->signature[1] != 'C' |
|
|| bh->signature[2] != 'I' |
|
|| bh->signature[3] != 'R' |
|
|| le16_to_cpu(bh->struct_length) < |
|
(u16)sizeof(struct esas2r_boot_header) |
|
|| bh->class_code[2] != 0x01 |
|
|| bh->class_code[1] != 0x04 |
|
|| bh->class_code[0] != 0x00 |
|
|| (bh->code_type != CODE_TYPE_PC |
|
&& bh->code_type != CODE_TYPE_OPEN |
|
&& bh->code_type != CODE_TYPE_EFI)) |
|
return 0xff; |
|
|
|
return bh->code_type; |
|
} |
|
|
|
/* The sum of all the WORDS of the image */ |
|
static u16 calc_fi_checksum(struct esas2r_flash_context *fc) |
|
{ |
|
struct esas2r_flash_img *fi = fc->fi; |
|
u16 cksum; |
|
u32 len; |
|
u16 *pw; |
|
|
|
for (len = (fi->length - fc->fi_hdr_len) / 2, |
|
pw = (u16 *)((u8 *)fi + fc->fi_hdr_len), |
|
cksum = 0; |
|
len; |
|
len--, pw++) |
|
cksum = cksum + le16_to_cpu(*pw); |
|
|
|
return cksum; |
|
} |
|
|
|
/* |
|
* Verify the flash image structure. The following verifications will |
|
* be performed: |
|
* 1) verify the fi_version is correct |
|
* 2) verify the checksum of the entire image. |
|
* 3) validate the adap_typ, action and length fields. |
|
* 4) validate each component header. check the img_type and |
|
* length fields |
|
* 5) validate each component image. validate signatures and |
|
* local checksums |
|
*/ |
|
static bool verify_fi(struct esas2r_adapter *a, |
|
struct esas2r_flash_context *fc) |
|
{ |
|
struct esas2r_flash_img *fi = fc->fi; |
|
u8 type; |
|
bool imgerr; |
|
u16 i; |
|
u32 len; |
|
struct esas2r_component_header *ch; |
|
|
|
/* Verify the length - length must even since we do a word checksum */ |
|
len = fi->length; |
|
|
|
if ((len & 1) |
|
|| len < fc->fi_hdr_len) { |
|
fi->status = FI_STAT_LENGTH; |
|
return false; |
|
} |
|
|
|
/* Get adapter type and verify type in flash image */ |
|
type = get_fi_adap_type(a); |
|
if ((type == FI_AT_UNKNWN) || (fi->adap_typ != type)) { |
|
fi->status = FI_STAT_ADAPTYP; |
|
return false; |
|
} |
|
|
|
/* |
|
* Loop through each component and verify the img_type and length |
|
* fields. Keep a running count of the sizes sooze we can verify total |
|
* size to additive size. |
|
*/ |
|
imgerr = false; |
|
|
|
for (i = 0, len = 0, ch = fi->cmp_hdr; |
|
i < fi->num_comps; |
|
i++, ch++) { |
|
bool cmperr = false; |
|
|
|
/* |
|
* Verify that the component header has the same index as the |
|
* image type. The headers must be ordered correctly |
|
*/ |
|
if (i != ch->img_type) { |
|
imgerr = true; |
|
ch->status = CH_STAT_INVALID; |
|
continue; |
|
} |
|
|
|
switch (ch->img_type) { |
|
case CH_IT_BIOS: |
|
type = CODE_TYPE_PC; |
|
break; |
|
|
|
case CH_IT_MAC: |
|
type = CODE_TYPE_OPEN; |
|
break; |
|
|
|
case CH_IT_EFI: |
|
type = CODE_TYPE_EFI; |
|
break; |
|
} |
|
|
|
switch (ch->img_type) { |
|
case CH_IT_FW: |
|
case CH_IT_NVR: |
|
break; |
|
|
|
case CH_IT_BIOS: |
|
case CH_IT_MAC: |
|
case CH_IT_EFI: |
|
if (ch->length & 0x1ff) |
|
cmperr = true; |
|
|
|
/* Test if component image is present */ |
|
if (ch->length == 0) |
|
break; |
|
|
|
/* Image is present - verify the image */ |
|
if (chk_boot((u8 *)fi + ch->image_offset, ch->length) |
|
!= type) |
|
cmperr = true; |
|
|
|
break; |
|
|
|
case CH_IT_CFG: |
|
|
|
/* Test if component image is present */ |
|
if (ch->length == 0) { |
|
cmperr = true; |
|
break; |
|
} |
|
|
|
/* Image is present - verify the image */ |
|
if (!chk_cfg((u8 *)fi + ch->image_offset + ch->length, |
|
ch->length, NULL)) |
|
cmperr = true; |
|
|
|
break; |
|
|
|
default: |
|
|
|
fi->status = FI_STAT_UNKNOWN; |
|
return false; |
|
} |
|
|
|
if (cmperr) { |
|
imgerr = true; |
|
ch->status = CH_STAT_INVALID; |
|
} else { |
|
ch->status = CH_STAT_PENDING; |
|
len += ch->length; |
|
} |
|
} |
|
|
|
if (imgerr) { |
|
fi->status = FI_STAT_MISSING; |
|
return false; |
|
} |
|
|
|
/* Compare fi->length to the sum of ch->length fields */ |
|
if (len != fi->length - fc->fi_hdr_len) { |
|
fi->status = FI_STAT_LENGTH; |
|
return false; |
|
} |
|
|
|
/* Compute the checksum - it should come out zero */ |
|
if (fi->checksum != calc_fi_checksum(fc)) { |
|
fi->status = FI_STAT_CHKSUM; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/* Fill in the FS IOCTL response data from a completed request. */ |
|
static void esas2r_complete_fs_ioctl(struct esas2r_adapter *a, |
|
struct esas2r_request *rq) |
|
{ |
|
struct esas2r_ioctl_fs *fs = |
|
(struct esas2r_ioctl_fs *)rq->interrupt_cx; |
|
|
|
if (rq->vrq->flash.sub_func == VDA_FLASH_COMMIT) |
|
esas2r_enable_heartbeat(a); |
|
|
|
fs->driver_error = rq->req_stat; |
|
|
|
if (fs->driver_error == RS_SUCCESS) |
|
fs->status = ATTO_STS_SUCCESS; |
|
else |
|
fs->status = ATTO_STS_FAILED; |
|
} |
|
|
|
/* Prepare an FS IOCTL request to be sent to the firmware. */ |
|
bool esas2r_process_fs_ioctl(struct esas2r_adapter *a, |
|
struct esas2r_ioctl_fs *fs, |
|
struct esas2r_request *rq, |
|
struct esas2r_sg_context *sgc) |
|
{ |
|
u8 cmdcnt = (u8)ARRAY_SIZE(cmd_to_fls_func); |
|
struct esas2r_ioctlfs_command *fsc = &fs->command; |
|
u8 func = 0; |
|
u32 datalen; |
|
|
|
fs->status = ATTO_STS_FAILED; |
|
fs->driver_error = RS_PENDING; |
|
|
|
if (fs->version > ESAS2R_FS_VER) { |
|
fs->status = ATTO_STS_INV_VERSION; |
|
return false; |
|
} |
|
|
|
if (fsc->command >= cmdcnt) { |
|
fs->status = ATTO_STS_INV_FUNC; |
|
return false; |
|
} |
|
|
|
func = cmd_to_fls_func[fsc->command]; |
|
if (func == 0xFF) { |
|
fs->status = ATTO_STS_INV_FUNC; |
|
return false; |
|
} |
|
|
|
if (fsc->command != ESAS2R_FS_CMD_CANCEL) { |
|
if ((a->pcid->device != ATTO_DID_MV_88RC9580 |
|
|| fs->adap_type != ESAS2R_FS_AT_ESASRAID2) |
|
&& (a->pcid->device != ATTO_DID_MV_88RC9580TS |
|
|| fs->adap_type != ESAS2R_FS_AT_TSSASRAID2) |
|
&& (a->pcid->device != ATTO_DID_MV_88RC9580TSE |
|
|| fs->adap_type != ESAS2R_FS_AT_TSSASRAID2E) |
|
&& (a->pcid->device != ATTO_DID_MV_88RC9580TL |
|
|| fs->adap_type != ESAS2R_FS_AT_TLSASHBA)) { |
|
fs->status = ATTO_STS_INV_ADAPTER; |
|
return false; |
|
} |
|
|
|
if (fs->driver_ver > ESAS2R_FS_DRVR_VER) { |
|
fs->status = ATTO_STS_INV_DRVR_VER; |
|
return false; |
|
} |
|
} |
|
|
|
if (test_bit(AF_DEGRADED_MODE, &a->flags)) { |
|
fs->status = ATTO_STS_DEGRADED; |
|
return false; |
|
} |
|
|
|
rq->interrupt_cb = esas2r_complete_fs_ioctl; |
|
rq->interrupt_cx = fs; |
|
datalen = le32_to_cpu(fsc->length); |
|
esas2r_build_flash_req(a, |
|
rq, |
|
func, |
|
fsc->checksum, |
|
le32_to_cpu(fsc->flash_addr), |
|
datalen); |
|
|
|
if (func == VDA_FLASH_WRITE |
|
|| func == VDA_FLASH_READ) { |
|
if (datalen == 0) { |
|
fs->status = ATTO_STS_INV_FUNC; |
|
return false; |
|
} |
|
|
|
esas2r_sgc_init(sgc, a, rq, rq->vrq->flash.data.sge); |
|
sgc->length = datalen; |
|
|
|
if (!esas2r_build_sg_list(a, rq, sgc)) { |
|
fs->status = ATTO_STS_OUT_OF_RSRC; |
|
return false; |
|
} |
|
} |
|
|
|
if (func == VDA_FLASH_COMMIT) |
|
esas2r_disable_heartbeat(a); |
|
|
|
esas2r_start_request(a, rq); |
|
|
|
return true; |
|
} |
|
|
|
static bool esas2r_flash_access(struct esas2r_adapter *a, u32 function) |
|
{ |
|
u32 starttime; |
|
u32 timeout; |
|
u32 intstat; |
|
u32 doorbell; |
|
|
|
/* Disable chip interrupts awhile */ |
|
if (function == DRBL_FLASH_REQ) |
|
esas2r_disable_chip_interrupts(a); |
|
|
|
/* Issue the request to the firmware */ |
|
esas2r_write_register_dword(a, MU_DOORBELL_IN, function); |
|
|
|
/* Now wait for the firmware to process it */ |
|
starttime = jiffies_to_msecs(jiffies); |
|
|
|
if (test_bit(AF_CHPRST_PENDING, &a->flags) || |
|
test_bit(AF_DISC_PENDING, &a->flags)) |
|
timeout = 40000; |
|
else |
|
timeout = 5000; |
|
|
|
while (true) { |
|
intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT); |
|
|
|
if (intstat & MU_INTSTAT_DRBL) { |
|
/* Got a doorbell interrupt. Check for the function */ |
|
doorbell = |
|
esas2r_read_register_dword(a, MU_DOORBELL_OUT); |
|
esas2r_write_register_dword(a, MU_DOORBELL_OUT, |
|
doorbell); |
|
if (doorbell & function) |
|
break; |
|
} |
|
|
|
schedule_timeout_interruptible(msecs_to_jiffies(100)); |
|
|
|
if ((jiffies_to_msecs(jiffies) - starttime) > timeout) { |
|
/* |
|
* Iimeout. If we were requesting flash access, |
|
* indicate we are done so the firmware knows we gave |
|
* up. If this was a REQ, we also need to re-enable |
|
* chip interrupts. |
|
*/ |
|
if (function == DRBL_FLASH_REQ) { |
|
esas2r_hdebug("flash access timeout"); |
|
esas2r_write_register_dword(a, MU_DOORBELL_IN, |
|
DRBL_FLASH_DONE); |
|
esas2r_enable_chip_interrupts(a); |
|
} else { |
|
esas2r_hdebug("flash release timeout"); |
|
} |
|
|
|
return false; |
|
} |
|
} |
|
|
|
/* if we're done, re-enable chip interrupts */ |
|
if (function == DRBL_FLASH_DONE) |
|
esas2r_enable_chip_interrupts(a); |
|
|
|
return true; |
|
} |
|
|
|
#define WINDOW_SIZE ((signed int)MW_DATA_WINDOW_SIZE) |
|
|
|
bool esas2r_read_flash_block(struct esas2r_adapter *a, |
|
void *to, |
|
u32 from, |
|
u32 size) |
|
{ |
|
u8 *end = (u8 *)to; |
|
|
|
/* Try to acquire access to the flash */ |
|
if (!esas2r_flash_access(a, DRBL_FLASH_REQ)) |
|
return false; |
|
|
|
while (size) { |
|
u32 len; |
|
u32 offset; |
|
u32 iatvr; |
|
|
|
if (test_bit(AF2_SERIAL_FLASH, &a->flags2)) |
|
iatvr = MW_DATA_ADDR_SER_FLASH + (from & -WINDOW_SIZE); |
|
else |
|
iatvr = MW_DATA_ADDR_PAR_FLASH + (from & -WINDOW_SIZE); |
|
|
|
esas2r_map_data_window(a, iatvr); |
|
offset = from & (WINDOW_SIZE - 1); |
|
len = size; |
|
|
|
if (len > WINDOW_SIZE - offset) |
|
len = WINDOW_SIZE - offset; |
|
|
|
from += len; |
|
size -= len; |
|
|
|
while (len--) { |
|
*end++ = esas2r_read_data_byte(a, offset); |
|
offset++; |
|
} |
|
} |
|
|
|
/* Release flash access */ |
|
esas2r_flash_access(a, DRBL_FLASH_DONE); |
|
return true; |
|
} |
|
|
|
bool esas2r_read_flash_rev(struct esas2r_adapter *a) |
|
{ |
|
u8 bytes[256]; |
|
u16 *pw; |
|
u16 *pwstart; |
|
u16 type; |
|
u16 size; |
|
u32 sz; |
|
|
|
sz = sizeof(bytes); |
|
pw = (u16 *)(bytes + sz); |
|
pwstart = (u16 *)bytes + 2; |
|
|
|
if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_CPYR - sz, sz)) |
|
goto invalid_rev; |
|
|
|
while (pw >= pwstart) { |
|
pw--; |
|
type = le16_to_cpu(*pw); |
|
pw--; |
|
size = le16_to_cpu(*pw); |
|
pw -= size / 2; |
|
|
|
if (type == FBT_CPYR |
|
|| type == FBT_SETUP |
|
|| pw < pwstart) |
|
continue; |
|
|
|
if (type == FBT_FLASH_VER) |
|
a->flash_ver = le32_to_cpu(*(u32 *)pw); |
|
|
|
break; |
|
} |
|
|
|
invalid_rev: |
|
return esas2r_print_flash_rev(a); |
|
} |
|
|
|
bool esas2r_print_flash_rev(struct esas2r_adapter *a) |
|
{ |
|
u16 year = LOWORD(a->flash_ver); |
|
u8 day = LOBYTE(HIWORD(a->flash_ver)); |
|
u8 month = HIBYTE(HIWORD(a->flash_ver)); |
|
|
|
if (day == 0 |
|
|| month == 0 |
|
|| day > 31 |
|
|| month > 12 |
|
|| year < 2006 |
|
|| year > 9999) { |
|
strcpy(a->flash_rev, "not found"); |
|
a->flash_ver = 0; |
|
return false; |
|
} |
|
|
|
sprintf(a->flash_rev, "%02d/%02d/%04d", month, day, year); |
|
esas2r_hdebug("flash version: %s", a->flash_rev); |
|
return true; |
|
} |
|
|
|
/* |
|
* Find the type of boot image type that is currently in the flash. |
|
* The chip only has a 64 KB PCI-e expansion ROM |
|
* size so only one image can be flashed at a time. |
|
*/ |
|
bool esas2r_read_image_type(struct esas2r_adapter *a) |
|
{ |
|
u8 bytes[256]; |
|
struct esas2r_boot_image *bi; |
|
struct esas2r_boot_header *bh; |
|
u32 sz; |
|
u32 len; |
|
u32 offset; |
|
|
|
/* Start at the base of the boot images and look for a valid image */ |
|
sz = sizeof(bytes); |
|
len = FLS_LENGTH_BOOT; |
|
offset = 0; |
|
|
|
while (true) { |
|
if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_BOOT + |
|
offset, |
|
sz)) |
|
goto invalid_rev; |
|
|
|
bi = (struct esas2r_boot_image *)bytes; |
|
bh = (struct esas2r_boot_header *)((u8 *)bi + |
|
le16_to_cpu( |
|
bi->header_offset)); |
|
if (bi->signature != cpu_to_le16(0xAA55)) |
|
goto invalid_rev; |
|
|
|
if (bh->code_type == CODE_TYPE_PC) { |
|
strcpy(a->image_type, "BIOS"); |
|
|
|
return true; |
|
} else if (bh->code_type == CODE_TYPE_EFI) { |
|
struct esas2r_efi_image *ei; |
|
|
|
/* |
|
* So we have an EFI image. There are several types |
|
* so see which architecture we have. |
|
*/ |
|
ei = (struct esas2r_efi_image *)bytes; |
|
|
|
switch (le16_to_cpu(ei->machine_type)) { |
|
case EFI_MACHINE_IA32: |
|
strcpy(a->image_type, "EFI 32-bit"); |
|
return true; |
|
|
|
case EFI_MACHINE_IA64: |
|
strcpy(a->image_type, "EFI itanium"); |
|
return true; |
|
|
|
case EFI_MACHINE_X64: |
|
strcpy(a->image_type, "EFI 64-bit"); |
|
return true; |
|
|
|
case EFI_MACHINE_EBC: |
|
strcpy(a->image_type, "EFI EBC"); |
|
return true; |
|
|
|
default: |
|
goto invalid_rev; |
|
} |
|
} else { |
|
u32 thislen; |
|
|
|
/* jump to the next image */ |
|
thislen = (u32)le16_to_cpu(bh->image_length) * 512; |
|
if (thislen == 0 |
|
|| thislen + offset > len |
|
|| bh->indicator == INDICATOR_LAST) |
|
break; |
|
|
|
offset += thislen; |
|
} |
|
} |
|
|
|
invalid_rev: |
|
strcpy(a->image_type, "no boot images"); |
|
return false; |
|
} |
|
|
|
/* |
|
* Read and validate current NVRAM parameters by accessing |
|
* physical NVRAM directly. if currently stored parameters are |
|
* invalid, use the defaults. |
|
*/ |
|
bool esas2r_nvram_read_direct(struct esas2r_adapter *a) |
|
{ |
|
bool result; |
|
|
|
if (down_interruptible(&a->nvram_semaphore)) |
|
return false; |
|
|
|
if (!esas2r_read_flash_block(a, a->nvram, FLS_OFFSET_NVR, |
|
sizeof(struct esas2r_sas_nvram))) { |
|
esas2r_hdebug("NVRAM read failed, using defaults"); |
|
up(&a->nvram_semaphore); |
|
return false; |
|
} |
|
|
|
result = esas2r_nvram_validate(a); |
|
|
|
up(&a->nvram_semaphore); |
|
|
|
return result; |
|
} |
|
|
|
/* Interrupt callback to process NVRAM completions. */ |
|
static void esas2r_nvram_callback(struct esas2r_adapter *a, |
|
struct esas2r_request *rq) |
|
{ |
|
struct atto_vda_flash_req *vrq = &rq->vrq->flash; |
|
|
|
if (rq->req_stat == RS_SUCCESS) { |
|
/* last request was successful. see what to do now. */ |
|
|
|
switch (vrq->sub_func) { |
|
case VDA_FLASH_BEGINW: |
|
vrq->sub_func = VDA_FLASH_WRITE; |
|
rq->req_stat = RS_PENDING; |
|
break; |
|
|
|
case VDA_FLASH_WRITE: |
|
vrq->sub_func = VDA_FLASH_COMMIT; |
|
rq->req_stat = RS_PENDING; |
|
break; |
|
|
|
case VDA_FLASH_READ: |
|
esas2r_nvram_validate(a); |
|
break; |
|
|
|
case VDA_FLASH_COMMIT: |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
if (rq->req_stat != RS_PENDING) { |
|
/* update the NVRAM state */ |
|
if (rq->req_stat == RS_SUCCESS) |
|
set_bit(AF_NVR_VALID, &a->flags); |
|
else |
|
clear_bit(AF_NVR_VALID, &a->flags); |
|
|
|
esas2r_enable_heartbeat(a); |
|
|
|
up(&a->nvram_semaphore); |
|
} |
|
} |
|
|
|
/* |
|
* Write the contents of nvram to the adapter's physical NVRAM. |
|
* The cached copy of the NVRAM is also updated. |
|
*/ |
|
bool esas2r_nvram_write(struct esas2r_adapter *a, struct esas2r_request *rq, |
|
struct esas2r_sas_nvram *nvram) |
|
{ |
|
struct esas2r_sas_nvram *n = nvram; |
|
u8 sas_address_bytes[8]; |
|
u32 *sas_address_dwords = (u32 *)&sas_address_bytes[0]; |
|
struct atto_vda_flash_req *vrq = &rq->vrq->flash; |
|
|
|
if (test_bit(AF_DEGRADED_MODE, &a->flags)) |
|
return false; |
|
|
|
if (down_interruptible(&a->nvram_semaphore)) |
|
return false; |
|
|
|
if (n == NULL) |
|
n = a->nvram; |
|
|
|
/* check the validity of the settings */ |
|
if (n->version > SASNVR_VERSION) { |
|
up(&a->nvram_semaphore); |
|
return false; |
|
} |
|
|
|
memcpy(&sas_address_bytes[0], n->sas_addr, 8); |
|
|
|
if (sas_address_bytes[0] != 0x50 |
|
|| sas_address_bytes[1] != 0x01 |
|
|| sas_address_bytes[2] != 0x08 |
|
|| (sas_address_bytes[3] & 0xF0) != 0x60 |
|
|| ((sas_address_bytes[3] & 0x0F) | sas_address_dwords[1]) == 0) { |
|
up(&a->nvram_semaphore); |
|
return false; |
|
} |
|
|
|
if (n->spin_up_delay > SASNVR_SPINUP_MAX) |
|
n->spin_up_delay = SASNVR_SPINUP_MAX; |
|
|
|
n->version = SASNVR_VERSION; |
|
n->checksum = n->checksum - esas2r_nvramcalc_cksum(n); |
|
memcpy(a->nvram, n, sizeof(struct esas2r_sas_nvram)); |
|
|
|
/* write the NVRAM */ |
|
n = a->nvram; |
|
esas2r_disable_heartbeat(a); |
|
|
|
esas2r_build_flash_req(a, |
|
rq, |
|
VDA_FLASH_BEGINW, |
|
esas2r_nvramcalc_xor_cksum(n), |
|
FLS_OFFSET_NVR, |
|
sizeof(struct esas2r_sas_nvram)); |
|
|
|
if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) { |
|
|
|
vrq->data.sge[0].length = |
|
cpu_to_le32(SGE_LAST | |
|
sizeof(struct esas2r_sas_nvram)); |
|
vrq->data.sge[0].address = cpu_to_le64( |
|
a->uncached_phys + (u64)((u8 *)n - a->uncached)); |
|
} else { |
|
vrq->data.prde[0].ctl_len = |
|
cpu_to_le32(sizeof(struct esas2r_sas_nvram)); |
|
vrq->data.prde[0].address = cpu_to_le64( |
|
a->uncached_phys |
|
+ (u64)((u8 *)n - a->uncached)); |
|
} |
|
rq->interrupt_cb = esas2r_nvram_callback; |
|
esas2r_start_request(a, rq); |
|
return true; |
|
} |
|
|
|
/* Validate the cached NVRAM. if the NVRAM is invalid, load the defaults. */ |
|
bool esas2r_nvram_validate(struct esas2r_adapter *a) |
|
{ |
|
struct esas2r_sas_nvram *n = a->nvram; |
|
bool rslt = false; |
|
|
|
if (n->signature[0] != 'E' |
|
|| n->signature[1] != 'S' |
|
|| n->signature[2] != 'A' |
|
|| n->signature[3] != 'S') { |
|
esas2r_hdebug("invalid NVRAM signature"); |
|
} else if (esas2r_nvramcalc_cksum(n)) { |
|
esas2r_hdebug("invalid NVRAM checksum"); |
|
} else if (n->version > SASNVR_VERSION) { |
|
esas2r_hdebug("invalid NVRAM version"); |
|
} else { |
|
set_bit(AF_NVR_VALID, &a->flags); |
|
rslt = true; |
|
} |
|
|
|
if (rslt == false) { |
|
esas2r_hdebug("using defaults"); |
|
esas2r_nvram_set_defaults(a); |
|
} |
|
|
|
return rslt; |
|
} |
|
|
|
/* |
|
* Set the cached NVRAM to defaults. note that this function sets the default |
|
* NVRAM when it has been determined that the physical NVRAM is invalid. |
|
* In this case, the SAS address is fabricated. |
|
*/ |
|
void esas2r_nvram_set_defaults(struct esas2r_adapter *a) |
|
{ |
|
struct esas2r_sas_nvram *n = a->nvram; |
|
u32 time = jiffies_to_msecs(jiffies); |
|
|
|
clear_bit(AF_NVR_VALID, &a->flags); |
|
*n = default_sas_nvram; |
|
n->sas_addr[3] |= 0x0F; |
|
n->sas_addr[4] = HIBYTE(LOWORD(time)); |
|
n->sas_addr[5] = LOBYTE(LOWORD(time)); |
|
n->sas_addr[6] = a->pcid->bus->number; |
|
n->sas_addr[7] = a->pcid->devfn; |
|
} |
|
|
|
void esas2r_nvram_get_defaults(struct esas2r_adapter *a, |
|
struct esas2r_sas_nvram *nvram) |
|
{ |
|
u8 sas_addr[8]; |
|
|
|
/* |
|
* in case we are copying the defaults into the adapter, copy the SAS |
|
* address out first. |
|
*/ |
|
memcpy(&sas_addr[0], a->nvram->sas_addr, 8); |
|
*nvram = default_sas_nvram; |
|
memcpy(&nvram->sas_addr[0], &sas_addr[0], 8); |
|
} |
|
|
|
bool esas2r_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi, |
|
struct esas2r_request *rq, struct esas2r_sg_context *sgc) |
|
{ |
|
struct esas2r_flash_context *fc = &a->flash_context; |
|
u8 j; |
|
struct esas2r_component_header *ch; |
|
|
|
if (test_and_set_bit(AF_FLASH_LOCK, &a->flags)) { |
|
/* flag was already set */ |
|
fi->status = FI_STAT_BUSY; |
|
return false; |
|
} |
|
|
|
memcpy(&fc->sgc, sgc, sizeof(struct esas2r_sg_context)); |
|
sgc = &fc->sgc; |
|
fc->fi = fi; |
|
fc->sgc_offset = sgc->cur_offset; |
|
rq->req_stat = RS_SUCCESS; |
|
rq->interrupt_cx = fc; |
|
|
|
switch (fi->fi_version) { |
|
case FI_VERSION_1: |
|
fc->scratch = ((struct esas2r_flash_img *)fi)->scratch_buf; |
|
fc->num_comps = FI_NUM_COMPS_V1; |
|
fc->fi_hdr_len = sizeof(struct esas2r_flash_img); |
|
break; |
|
|
|
default: |
|
return complete_fmapi_req(a, rq, FI_STAT_IMG_VER); |
|
} |
|
|
|
if (test_bit(AF_DEGRADED_MODE, &a->flags)) |
|
return complete_fmapi_req(a, rq, FI_STAT_DEGRADED); |
|
|
|
switch (fi->action) { |
|
case FI_ACT_DOWN: /* Download the components */ |
|
/* Verify the format of the flash image */ |
|
if (!verify_fi(a, fc)) |
|
return complete_fmapi_req(a, rq, fi->status); |
|
|
|
/* Adjust the BIOS fields that are dependent on the HBA */ |
|
ch = &fi->cmp_hdr[CH_IT_BIOS]; |
|
|
|
if (ch->length) |
|
fix_bios(a, fi); |
|
|
|
/* Adjust the EFI fields that are dependent on the HBA */ |
|
ch = &fi->cmp_hdr[CH_IT_EFI]; |
|
|
|
if (ch->length) |
|
fix_efi(a, fi); |
|
|
|
/* |
|
* Since the image was just modified, compute the checksum on |
|
* the modified image. First update the CRC for the composite |
|
* expansion ROM image. |
|
*/ |
|
fi->checksum = calc_fi_checksum(fc); |
|
|
|
/* Disable the heartbeat */ |
|
esas2r_disable_heartbeat(a); |
|
|
|
/* Now start up the download sequence */ |
|
fc->task = FMTSK_ERASE_BOOT; |
|
fc->func = VDA_FLASH_BEGINW; |
|
fc->comp_typ = CH_IT_CFG; |
|
fc->flsh_addr = FLS_OFFSET_BOOT; |
|
fc->sgc.length = FLS_LENGTH_BOOT; |
|
fc->sgc.cur_offset = NULL; |
|
|
|
/* Setup the callback address */ |
|
fc->interrupt_cb = fw_download_proc; |
|
break; |
|
|
|
case FI_ACT_UPSZ: /* Get upload sizes */ |
|
fi->adap_typ = get_fi_adap_type(a); |
|
fi->flags = 0; |
|
fi->num_comps = fc->num_comps; |
|
fi->length = fc->fi_hdr_len; |
|
|
|
/* Report the type of boot image in the rel_version string */ |
|
memcpy(fi->rel_version, a->image_type, |
|
sizeof(fi->rel_version)); |
|
|
|
/* Build the component headers */ |
|
for (j = 0, ch = fi->cmp_hdr; |
|
j < fi->num_comps; |
|
j++, ch++) { |
|
ch->img_type = j; |
|
ch->status = CH_STAT_PENDING; |
|
ch->length = 0; |
|
ch->version = 0xffffffff; |
|
ch->image_offset = 0; |
|
ch->pad[0] = 0; |
|
ch->pad[1] = 0; |
|
} |
|
|
|
if (a->flash_ver != 0) { |
|
fi->cmp_hdr[CH_IT_BIOS].version = |
|
fi->cmp_hdr[CH_IT_MAC].version = |
|
fi->cmp_hdr[CH_IT_EFI].version = |
|
fi->cmp_hdr[CH_IT_CFG].version |
|
= a->flash_ver; |
|
|
|
fi->cmp_hdr[CH_IT_BIOS].status = |
|
fi->cmp_hdr[CH_IT_MAC].status = |
|
fi->cmp_hdr[CH_IT_EFI].status = |
|
fi->cmp_hdr[CH_IT_CFG].status = |
|
CH_STAT_SUCCESS; |
|
|
|
return complete_fmapi_req(a, rq, FI_STAT_SUCCESS); |
|
} |
|
|
|
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; |
|
}
|
|
|