Commit a28d9e4e authored by Joe Carnuccio's avatar Joe Carnuccio Committed by Martin K. Petersen

scsi: qla2xxx: Add support for multiple fwdump templates/segments

This patch adds multipe firmware dump template and segments support for
ISP27XX/28XX.
Signed-off-by: default avatarJoe Carnuccio <joe.carnuccio@cavium.com>
Signed-off-by: default avatarHimanshu Madhani <hmadhani@marvell.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent f8f97b0c
...@@ -84,8 +84,7 @@ qla24xx_fcp_prio_cfg_valid(scsi_qla_host_t *vha, ...@@ -84,8 +84,7 @@ qla24xx_fcp_prio_cfg_valid(scsi_qla_host_t *vha,
return 0; return 0;
} }
if (bcode[0] != 'H' || bcode[1] != 'Q' || bcode[2] != 'O' || if (memcmp(bcode, "HQOS", 4)) {
bcode[3] != 'S') {
/* Invalid FCP priority data header*/ /* Invalid FCP priority data header*/
ql_dbg(ql_dbg_user, vha, 0x7052, ql_dbg(ql_dbg_user, vha, 0x7052,
"Invalid FCP Priority data header. bcode=0x%x.\n", "Invalid FCP Priority data header. bcode=0x%x.\n",
......
...@@ -4030,9 +4030,11 @@ struct qla_hw_data { ...@@ -4030,9 +4030,11 @@ struct qla_hw_data {
uint8_t pep_version[3]; uint8_t pep_version[3];
/* Firmware dump template */ /* Firmware dump template */
void *fw_dump_template; struct fwdt {
uint32_t fw_dump_template_len; void *template;
/* Firmware dump information. */ ulong length;
ulong dump_size;
} fwdt[2];
struct qla2xxx_fw_dump *fw_dump; struct qla2xxx_fw_dump *fw_dump;
uint32_t fw_dump_len; uint32_t fw_dump_len;
bool fw_dumped; bool fw_dumped;
...@@ -4075,7 +4077,6 @@ struct qla_hw_data { ...@@ -4075,7 +4077,6 @@ struct qla_hw_data {
uint16_t product_id[4]; uint16_t product_id[4];
uint8_t model_number[16+1]; uint8_t model_number[16+1];
#define BINZERO "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
char model_desc[80]; char model_desc[80];
uint8_t adapter_id[16+1]; uint8_t adapter_id[16+1];
......
...@@ -611,7 +611,7 @@ extern void qla82xx_fw_dump(scsi_qla_host_t *, int); ...@@ -611,7 +611,7 @@ extern void qla82xx_fw_dump(scsi_qla_host_t *, int);
extern void qla8044_fw_dump(scsi_qla_host_t *, int); extern void qla8044_fw_dump(scsi_qla_host_t *, int);
extern void qla27xx_fwdump(scsi_qla_host_t *, int); extern void qla27xx_fwdump(scsi_qla_host_t *, int);
extern ulong qla27xx_fwdt_calculate_dump_size(struct scsi_qla_host *); extern ulong qla27xx_fwdt_calculate_dump_size(struct scsi_qla_host *, void *);
extern int qla27xx_fwdt_template_valid(void *); extern int qla27xx_fwdt_template_valid(void *);
extern ulong qla27xx_fwdt_template_size(void *); extern ulong qla27xx_fwdt_template_size(void *);
......
...@@ -3089,12 +3089,15 @@ qla2x00_alloc_offload_mem(scsi_qla_host_t *vha) ...@@ -3089,12 +3089,15 @@ qla2x00_alloc_offload_mem(scsi_qla_host_t *vha)
void void
qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
{ {
int rval;
uint32_t dump_size, fixed_size, mem_size, req_q_size, rsp_q_size, uint32_t dump_size, fixed_size, mem_size, req_q_size, rsp_q_size,
eft_size, fce_size, mq_size; eft_size, fce_size, mq_size;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct req_que *req = ha->req_q_map[0]; struct req_que *req = ha->req_q_map[0];
struct rsp_que *rsp = ha->rsp_q_map[0]; struct rsp_que *rsp = ha->rsp_q_map[0];
struct qla2xxx_fw_dump *fw_dump; struct qla2xxx_fw_dump *fw_dump;
dma_addr_t tc_dma;
void *tc;
dump_size = fixed_size = mem_size = eft_size = fce_size = mq_size = 0; dump_size = fixed_size = mem_size = eft_size = fce_size = mq_size = 0;
req_q_size = rsp_q_size = 0; req_q_size = rsp_q_size = 0;
...@@ -3139,20 +3142,51 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) ...@@ -3139,20 +3142,51 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
fce_size = sizeof(struct qla2xxx_fce_chain) + FCE_SIZE; fce_size = sizeof(struct qla2xxx_fce_chain) + FCE_SIZE;
try_eft: try_eft:
if (ha->eft)
dma_free_coherent(&ha->pdev->dev,
EFT_SIZE, ha->eft, ha->eft_dma);
/* Allocate memory for Extended Trace Buffer. */
tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma,
GFP_KERNEL);
if (!tc) {
ql_log(ql_log_warn, vha, 0x00c1,
"Unable to allocate (%d KB) for EFT.\n",
EFT_SIZE / 1024);
goto allocate;
}
rval = qla2x00_enable_eft_trace(vha, tc_dma, EFT_NUM_BUFFERS);
if (rval) {
ql_log(ql_log_warn, vha, 0x00c2,
"Unable to initialize EFT (%d).\n", rval);
dma_free_coherent(&ha->pdev->dev, EFT_SIZE, tc,
tc_dma);
}
ql_dbg(ql_dbg_init, vha, 0x00c3, ql_dbg(ql_dbg_init, vha, 0x00c3,
"Allocated (%d KB) EFT ...\n", EFT_SIZE / 1024); "Allocated (%d KB) EFT ...\n", EFT_SIZE / 1024);
eft_size = EFT_SIZE; eft_size = EFT_SIZE;
} }
if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
if (!ha->fw_dump_template) { struct fwdt *fwdt = ha->fwdt;
uint j;
for (j = 0; j < 2; j++, fwdt++) {
if (!fwdt->template) {
ql_log(ql_log_warn, vha, 0x00ba, ql_log(ql_log_warn, vha, 0x00ba,
"Failed missing fwdump template\n"); "-> fwdt%u no template\n", j);
return; continue;
} }
dump_size = qla27xx_fwdt_calculate_dump_size(vha);
ql_dbg(ql_dbg_init, vha, 0x00fa, ql_dbg(ql_dbg_init, vha, 0x00fa,
"-> allocating fwdump (%x bytes)...\n", dump_size); "-> fwdt%u calculating fwdump size...\n", j);
fwdt->dump_size = qla27xx_fwdt_calculate_dump_size(
vha, fwdt->template);
ql_dbg(ql_dbg_init, vha, 0x00fa,
"-> fwdt%u calculated fwdump size = %#lx bytes\n",
j, fwdt->dump_size);
dump_size += fwdt->dump_size;
}
goto allocate; goto allocate;
} }
...@@ -4270,11 +4304,14 @@ qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len, ...@@ -4270,11 +4304,14 @@ qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len,
{ {
char *st, *en; char *st, *en;
uint16_t index; uint16_t index;
uint64_t zero[2] = { 0 };
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
int use_tbl = !IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && int use_tbl = !IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
!IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha); !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha);
if (memcmp(model, BINZERO, len) != 0) { if (len > sizeof(zero))
len = sizeof(zero);
if (memcmp(model, &zero, len) != 0) {
strncpy(ha->model_number, model, len); strncpy(ha->model_number, model, len);
st = en = ha->model_number; st = en = ha->model_number;
en += len - 1; en += len - 1;
...@@ -4378,8 +4415,8 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) ...@@ -4378,8 +4415,8 @@ qla2x00_nvram_config(scsi_qla_host_t *vha)
nv, ha->nvram_size); nv, ha->nvram_size);
/* Bad NVRAM data, set defaults parameters. */ /* Bad NVRAM data, set defaults parameters. */
if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || if (chksum || memcmp("ISP ", nv->id, sizeof(nv->id)) ||
nv->id[2] != 'P' || nv->id[3] != ' ' || nv->nvram_version < 1) { nv->nvram_version < 1) {
/* Reset NVRAM data. */ /* Reset NVRAM data. */
ql_log(ql_log_warn, vha, 0x0064, ql_log(ql_log_warn, vha, 0x0064,
"Inconsistent NVRAM " "Inconsistent NVRAM "
...@@ -6986,9 +7023,8 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) ...@@ -6986,9 +7023,8 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
nv, ha->nvram_size); nv, ha->nvram_size);
/* Bad NVRAM data, set defaults parameters. */ /* Bad NVRAM data, set defaults parameters. */
if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P' if (chksum || memcmp("ISP ", nv->id, sizeof(nv->id)) ||
|| nv->id[3] != ' ' || le16_to_cpu(nv->nvram_version) < ICB_VERSION) {
nv->nvram_version < cpu_to_le16(ICB_VERSION)) {
/* Reset NVRAM data. */ /* Reset NVRAM data. */
ql_log(ql_log_warn, vha, 0x006b, ql_log(ql_log_warn, vha, 0x006b,
"Inconsistent NVRAM detected: checksum=0x%x id=%c " "Inconsistent NVRAM detected: checksum=0x%x id=%c "
...@@ -7304,14 +7340,16 @@ static int ...@@ -7304,14 +7340,16 @@ static int
qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr, qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
uint32_t faddr) uint32_t faddr)
{ {
int rval = QLA_SUCCESS; int rval;
int segments, fragment; uint templates, segments, fragment;
uint32_t *dcode, dlen; ulong i;
uint32_t risc_addr; uint j;
uint32_t risc_size; ulong dlen;
uint32_t i; uint32_t *dcode;
uint32_t risc_addr, risc_size, risc_attr = 0;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct req_que *req = ha->req_q_map[0]; struct req_que *req = ha->req_q_map[0];
struct fwdt *fwdt = ha->fwdt;
ql_dbg(ql_dbg_init, vha, 0x008b, ql_dbg(ql_dbg_init, vha, 0x008b,
"FW: Loading firmware from flash (%x).\n", faddr); "FW: Loading firmware from flash (%x).\n", faddr);
...@@ -7329,34 +7367,36 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr, ...@@ -7329,34 +7367,36 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
return QLA_FUNCTION_FAILED; return QLA_FUNCTION_FAILED;
} }
while (segments && rval == QLA_SUCCESS) { dcode = (void *)req->ring;
/* Read segment's load information. */ *srisc_addr = 0;
qla24xx_read_flash_data(vha, dcode, faddr, 4); segments = FA_RISC_CODE_SEGMENTS;
for (j = 0; j < segments; j++) {
ql_dbg(ql_dbg_init, vha, 0x008d,
"-> Loading segment %u...\n", j);
qla24xx_read_flash_data(vha, dcode, faddr, 10);
risc_addr = be32_to_cpu(dcode[2]); risc_addr = be32_to_cpu(dcode[2]);
*srisc_addr = *srisc_addr == 0 ? risc_addr : *srisc_addr;
risc_size = be32_to_cpu(dcode[3]); risc_size = be32_to_cpu(dcode[3]);
if (!*srisc_addr) {
*srisc_addr = risc_addr;
risc_attr = be32_to_cpu(dcode[9]);
}
fragment = 0; dlen = ha->fw_transfer_size >> 2;
while (risc_size > 0 && rval == QLA_SUCCESS) { for (fragment = 0; risc_size; fragment++) {
dlen = (uint32_t)(ha->fw_transfer_size >> 2);
if (dlen > risc_size) if (dlen > risc_size)
dlen = risc_size; dlen = risc_size;
ql_dbg(ql_dbg_init, vha, 0x008e, ql_dbg(ql_dbg_init, vha, 0x008e,
"Loading risc segment@ risc addr %x " "-> Loading fragment %u: %#x <- %#x (%#lx dwords)...\n",
"number of dwords 0x%x offset 0x%x.\n", fragment, risc_addr, faddr, dlen);
risc_addr, dlen, faddr);
qla24xx_read_flash_data(vha, dcode, faddr, dlen); qla24xx_read_flash_data(vha, dcode, faddr, dlen);
for (i = 0; i < dlen; i++) for (i = 0; i < dlen; i++)
dcode[i] = swab32(dcode[i]); dcode[i] = swab32(dcode[i]);
rval = qla2x00_load_ram(vha, req->dma, risc_addr, rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen);
dlen);
if (rval) { if (rval) {
ql_log(ql_log_fatal, vha, 0x008f, ql_log(ql_log_fatal, vha, 0x008f,
"Failed to load segment %d of firmware.\n", "-> Failed load firmware fragment %u.\n",
fragment); fragment);
return QLA_FUNCTION_FAILED; return QLA_FUNCTION_FAILED;
} }
...@@ -7364,72 +7404,83 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr, ...@@ -7364,72 +7404,83 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
faddr += dlen; faddr += dlen;
risc_addr += dlen; risc_addr += dlen;
risc_size -= dlen; risc_size -= dlen;
fragment++;
} }
/* Next segment. */
segments--;
} }
if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
return rval; return QLA_SUCCESS;
if (ha->fw_dump_template) templates = (risc_attr & BIT_9) ? 2 : 1;
vfree(ha->fw_dump_template); ql_dbg(ql_dbg_init, vha, 0x0160, "-> templates = %u\n", templates);
ha->fw_dump_template = NULL; for (j = 0; j < templates; j++, fwdt++) {
ha->fw_dump_template_len = 0; if (fwdt->template)
vfree(fwdt->template);
fwdt->template = NULL;
fwdt->length = 0;
ql_dbg(ql_dbg_init, vha, 0x0161,
"Loading fwdump template from %x\n", faddr);
qla24xx_read_flash_data(vha, dcode, faddr, 7); qla24xx_read_flash_data(vha, dcode, faddr, 7);
risc_size = be32_to_cpu(dcode[2]); risc_size = be32_to_cpu(dcode[2]);
ql_dbg(ql_dbg_init, vha, 0x0161,
"-> fwdt%u template array at %#x (%#x dwords)\n",
j, faddr, risc_size);
if (!risc_size || !~risc_size) {
ql_dbg(ql_dbg_init, vha, 0x0162, ql_dbg(ql_dbg_init, vha, 0x0162,
"-> array size %x dwords\n", risc_size); "-> fwdt%u failed to read array\n", j);
if (risc_size == 0 || risc_size == ~0)
goto failed; goto failed;
}
/* skip header and ignore checksum */
faddr += 7;
risc_size -= 8;
dlen = (risc_size - 8) * sizeof(*dcode);
ql_dbg(ql_dbg_init, vha, 0x0163, ql_dbg(ql_dbg_init, vha, 0x0163,
"-> template allocating %x bytes...\n", dlen); "-> fwdt%u template allocate template %#x words...\n",
ha->fw_dump_template = vmalloc(dlen); j, risc_size);
if (!ha->fw_dump_template) { fwdt->template = vmalloc(risc_size * sizeof(*dcode));
if (!fwdt->template) {
ql_log(ql_log_warn, vha, 0x0164, ql_log(ql_log_warn, vha, 0x0164,
"Failed fwdump template allocate %x bytes.\n", risc_size); "-> fwdt%u failed allocate template.\n", j);
goto failed; goto failed;
} }
faddr += 7; dcode = fwdt->template;
risc_size -= 8;
dcode = ha->fw_dump_template;
qla24xx_read_flash_data(vha, dcode, faddr, risc_size); qla24xx_read_flash_data(vha, dcode, faddr, risc_size);
for (i = 0; i < risc_size; i++) for (i = 0; i < risc_size; i++)
dcode[i] = le32_to_cpu(dcode[i]); dcode[i] = le32_to_cpu(dcode[i]);
if (!qla27xx_fwdt_template_valid(dcode)) { if (!qla27xx_fwdt_template_valid(dcode)) {
ql_log(ql_log_warn, vha, 0x0165, ql_log(ql_log_warn, vha, 0x0165,
"Failed fwdump template validate\n"); "-> fwdt%u failed template validate\n", j);
goto failed; goto failed;
} }
dlen = qla27xx_fwdt_template_size(dcode); dlen = qla27xx_fwdt_template_size(dcode);
ql_dbg(ql_dbg_init, vha, 0x0166, ql_dbg(ql_dbg_init, vha, 0x0166,
"-> template size %x bytes\n", dlen); "-> fwdt%u template size %#lx bytes (%#lx words)\n",
j, dlen, dlen / sizeof(*dcode));
if (dlen > risc_size * sizeof(*dcode)) { if (dlen > risc_size * sizeof(*dcode)) {
ql_log(ql_log_warn, vha, 0x0167, ql_log(ql_log_warn, vha, 0x0167,
"Failed fwdump template exceeds array by %zx bytes\n", "-> fwdt%u template exceeds array (%-lu bytes)\n",
(size_t)(dlen - risc_size * sizeof(*dcode))); j, dlen - risc_size * sizeof(*dcode));
goto failed; goto failed;
} }
ha->fw_dump_template_len = dlen;
return rval; fwdt->length = dlen;
ql_dbg(ql_dbg_init, vha, 0x0168,
"-> fwdt%u loaded template ok\n", j);
faddr += risc_size + 1;
}
return QLA_SUCCESS;
failed: failed:
ql_log(ql_log_warn, vha, 0x016d, "Failed fwdump template\n"); if (fwdt->template)
if (ha->fw_dump_template) vfree(fwdt->template);
vfree(ha->fw_dump_template); fwdt->template = NULL;
ha->fw_dump_template = NULL; fwdt->length = 0;
ha->fw_dump_template_len = 0;
return rval; return QLA_SUCCESS;
} }
#define QLA_FW_URL "http://ldriver.qlogic.com/firmware/" #define QLA_FW_URL "http://ldriver.qlogic.com/firmware/"
...@@ -7537,31 +7588,31 @@ static int ...@@ -7537,31 +7588,31 @@ static int
qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
{ {
int rval; int rval;
int segments, fragment; uint templates, segments, fragment;
uint32_t *dcode, dlen; uint32_t *dcode;
uint32_t risc_addr; ulong dlen;
uint32_t risc_size; uint32_t risc_addr, risc_size, risc_attr = 0;
uint32_t i; ulong i;
uint j;
struct fw_blob *blob; struct fw_blob *blob;
uint32_t *fwcode; uint32_t *fwcode;
uint32_t fwclen;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct req_que *req = ha->req_q_map[0]; struct req_que *req = ha->req_q_map[0];
struct fwdt *fwdt = ha->fwdt;
ql_dbg(ql_dbg_init, vha, 0x0090,
"-> FW: Loading via request-firmware.\n");
/* Load firmware blob. */
blob = qla2x00_request_firmware(vha); blob = qla2x00_request_firmware(vha);
if (!blob) { if (!blob) {
ql_log(ql_log_warn, vha, 0x0090, ql_log(ql_log_warn, vha, 0x0092,
"Firmware image unavailable.\n"); "-> Firmware file not found.\n");
ql_log(ql_log_warn, vha, 0x0091,
"Firmware images can be retrieved from: "
QLA_FW_URL ".\n");
return QLA_FUNCTION_FAILED; return QLA_FUNCTION_FAILED;
} }
fwcode = (void *)blob->fw->data; fwcode = (void *)blob->fw->data;
dcode = fwcode; dcode = fwcode + 4;
if (qla24xx_risc_firmware_invalid(dcode)) { if (qla24xx_risc_firmware_invalid(dcode)) {
ql_log(ql_log_fatal, vha, 0x0093, ql_log(ql_log_fatal, vha, 0x0093,
"Unable to verify integrity of firmware image (%zd).\n", "Unable to verify integrity of firmware image (%zd).\n",
...@@ -7583,38 +7634,39 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) ...@@ -7583,38 +7634,39 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
return QLA_FUNCTION_FAILED; return QLA_FUNCTION_FAILED;
} }
while (segments && rval == QLA_SUCCESS) { dcode = (void *)req->ring;
*srisc_addr = 0;
segments = FA_RISC_CODE_SEGMENTS;
for (j = 0; j < segments; j++) {
ql_dbg(ql_dbg_init, vha, 0x0096,
"-> Loading segment %u...\n", j);
risc_addr = be32_to_cpu(fwcode[2]); risc_addr = be32_to_cpu(fwcode[2]);
*srisc_addr = *srisc_addr == 0 ? risc_addr : *srisc_addr;
risc_size = be32_to_cpu(fwcode[3]); risc_size = be32_to_cpu(fwcode[3]);
/* Validate firmware image size. */ if (!*srisc_addr) {
fwclen += risc_size * sizeof(uint32_t); *srisc_addr = risc_addr;
if (blob->fw->size < fwclen) { risc_attr = be32_to_cpu(fwcode[9]);
ql_log(ql_log_fatal, vha, 0x0096,
"Unable to verify integrity of firmware image "
"(%zd).\n", blob->fw->size);
return QLA_FUNCTION_FAILED;
} }
fragment = 0; dlen = ha->fw_transfer_size >> 2;
while (risc_size > 0 && rval == QLA_SUCCESS) { for (fragment = 0; risc_size; fragment++) {
dlen = (uint32_t)(ha->fw_transfer_size >> 2); dlen = (uint32_t)(ha->fw_transfer_size >> 2);
if (dlen > risc_size) if (dlen > risc_size)
dlen = risc_size; dlen = risc_size;
ql_dbg(ql_dbg_init, vha, 0x0097, ql_dbg(ql_dbg_init, vha, 0x0097,
"Loading risc segment@ risc addr %x " "-> Loading fragment %u: %#x <- %#x (%#lx words)...\n",
"number of dwords 0x%x.\n", risc_addr, dlen); fragment, risc_addr,
(uint32_t)(fwcode - (typeof(fwcode))blob->fw->data),
dlen);
for (i = 0; i < dlen; i++) for (i = 0; i < dlen; i++)
dcode[i] = swab32(fwcode[i]); dcode[i] = swab32(fwcode[i]);
rval = qla2x00_load_ram(vha, req->dma, risc_addr, rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen);
dlen);
if (rval) { if (rval) {
ql_log(ql_log_fatal, vha, 0x0098, ql_log(ql_log_fatal, vha, 0x0098,
"Failed to load segment %d of firmware.\n", "-> Failed load firmware fragment %u.\n",
fragment); fragment);
return QLA_FUNCTION_FAILED; return QLA_FUNCTION_FAILED;
} }
...@@ -7622,71 +7674,82 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) ...@@ -7622,71 +7674,82 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
fwcode += dlen; fwcode += dlen;
risc_addr += dlen; risc_addr += dlen;
risc_size -= dlen; risc_size -= dlen;
fragment++;
} }
/* Next segment. */
segments--;
} }
if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
return rval; return QLA_SUCCESS;
if (ha->fw_dump_template) templates = (risc_attr & BIT_9) ? 2 : 1;
vfree(ha->fw_dump_template); ql_dbg(ql_dbg_init, vha, 0x0170, "-> templates = %u\n", templates);
ha->fw_dump_template = NULL; for (j = 0; j < templates; j++, fwdt++) {
ha->fw_dump_template_len = 0; if (fwdt->template)
vfree(fwdt->template);
fwdt->template = NULL;
fwdt->length = 0;
ql_dbg(ql_dbg_init, vha, 0x171,
"Loading fwdump template from %x\n",
(uint32_t)((void *)fwcode - (void *)blob->fw->data));
risc_size = be32_to_cpu(fwcode[2]); risc_size = be32_to_cpu(fwcode[2]);
ql_dbg(ql_dbg_init, vha, 0x172, ql_dbg(ql_dbg_init, vha, 0x0171,
"-> array size %x dwords\n", risc_size); "-> fwdt%u template array at %#x (%#x dwords)\n",
if (risc_size == 0 || risc_size == ~0) j, (uint32_t)((void *)fwcode - (void *)blob->fw->data),
risc_size);
if (!risc_size || !~risc_size) {
ql_dbg(ql_dbg_init, vha, 0x0172,
"-> fwdt%u failed to read array\n", j);
goto failed; goto failed;
}
/* skip header and ignore checksum */
fwcode += 7;
risc_size -= 8;
dlen = (risc_size - 8) * sizeof(*fwcode);
ql_dbg(ql_dbg_init, vha, 0x0173, ql_dbg(ql_dbg_init, vha, 0x0173,
"-> template allocating %x bytes...\n", dlen); "-> fwdt%u template allocate template %#x words...\n",
ha->fw_dump_template = vmalloc(dlen); j, risc_size);
if (!ha->fw_dump_template) { fwdt->template = vmalloc(risc_size * sizeof(*dcode));
if (!fwdt->template) {
ql_log(ql_log_warn, vha, 0x0174, ql_log(ql_log_warn, vha, 0x0174,
"Failed fwdump template allocate %x bytes.\n", risc_size); "-> fwdt%u failed allocate template.\n", j);
goto failed; goto failed;
} }
fwcode += 7; dcode = fwdt->template;
risc_size -= 8;
dcode = ha->fw_dump_template;
for (i = 0; i < risc_size; i++) for (i = 0; i < risc_size; i++)
dcode[i] = le32_to_cpu(fwcode[i]); dcode[i] = le32_to_cpu(fwcode[i]);
if (!qla27xx_fwdt_template_valid(dcode)) { if (!qla27xx_fwdt_template_valid(dcode)) {
ql_log(ql_log_warn, vha, 0x0175, ql_log(ql_log_warn, vha, 0x0175,
"Failed fwdump template validate\n"); "-> fwdt%u failed template validate\n", j);
goto failed; goto failed;
} }
dlen = qla27xx_fwdt_template_size(dcode); dlen = qla27xx_fwdt_template_size(dcode);
ql_dbg(ql_dbg_init, vha, 0x0176, ql_dbg(ql_dbg_init, vha, 0x0176,
"-> template size %x bytes\n", dlen); "-> fwdt%u template size %#lx bytes (%#lx words)\n",
if (dlen > risc_size * sizeof(*fwcode)) { j, dlen, dlen / sizeof(*dcode));
if (dlen > risc_size * sizeof(*dcode)) {
ql_log(ql_log_warn, vha, 0x0177, ql_log(ql_log_warn, vha, 0x0177,
"Failed fwdump template exceeds array by %zx bytes\n", "-> fwdt%u template exceeds array (%-lu bytes)\n",
(size_t)(dlen - risc_size * sizeof(*fwcode))); j, dlen - risc_size * sizeof(*dcode));
goto failed; goto failed;
} }
ha->fw_dump_template_len = dlen;
return rval; fwdt->length = dlen;
ql_dbg(ql_dbg_init, vha, 0x0178,
"-> fwdt%u loaded template ok\n", j);
fwcode += risc_size + 1;
}
return QLA_SUCCESS;
failed: failed:
ql_log(ql_log_warn, vha, 0x017d, "Failed fwdump template\n"); if (fwdt->template)
if (ha->fw_dump_template) vfree(fwdt->template);
vfree(ha->fw_dump_template); fwdt->template = NULL;
ha->fw_dump_template = NULL; fwdt->length = 0;
ha->fw_dump_template_len = 0;
return rval; return QLA_SUCCESS;
} }
int int
...@@ -7953,9 +8016,8 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) ...@@ -7953,9 +8016,8 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
nv, ha->nvram_size); nv, ha->nvram_size);
/* Bad NVRAM data, set defaults parameters. */ /* Bad NVRAM data, set defaults parameters. */
if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P' if (chksum || memcmp("ISP ", nv->id, sizeof(nv->id)) ||
|| nv->id[3] != ' ' || le16_to_cpu(nv->nvram_version) < ICB_VERSION) {
nv->nvram_version < cpu_to_le16(ICB_VERSION)) {
/* Reset NVRAM data. */ /* Reset NVRAM data. */
ql_log(ql_log_info, vha, 0x0073, ql_log(ql_log_info, vha, 0x0073,
"Inconsistent NVRAM detected: checksum=0x%x id=%c " "Inconsistent NVRAM detected: checksum=0x%x id=%c "
......
...@@ -4647,6 +4647,9 @@ qla2x00_free_exchoffld_buffer(struct qla_hw_data *ha) ...@@ -4647,6 +4647,9 @@ qla2x00_free_exchoffld_buffer(struct qla_hw_data *ha)
static void static void
qla2x00_free_fw_dump(struct qla_hw_data *ha) qla2x00_free_fw_dump(struct qla_hw_data *ha)
{ {
struct fwdt *fwdt = ha->fwdt;
uint j;
if (ha->fce) if (ha->fce)
dma_free_coherent(&ha->pdev->dev, dma_free_coherent(&ha->pdev->dev,
FCE_SIZE, ha->fce, ha->fce_dma); FCE_SIZE, ha->fce, ha->fce_dma);
...@@ -4657,8 +4660,6 @@ qla2x00_free_fw_dump(struct qla_hw_data *ha) ...@@ -4657,8 +4660,6 @@ qla2x00_free_fw_dump(struct qla_hw_data *ha)
if (ha->fw_dump) if (ha->fw_dump)
vfree(ha->fw_dump); vfree(ha->fw_dump);
if (ha->fw_dump_template)
vfree(ha->fw_dump_template);
ha->fce = NULL; ha->fce = NULL;
ha->fce_dma = 0; ha->fce_dma = 0;
...@@ -4669,8 +4670,13 @@ qla2x00_free_fw_dump(struct qla_hw_data *ha) ...@@ -4669,8 +4670,13 @@ qla2x00_free_fw_dump(struct qla_hw_data *ha)
ha->fw_dump_reading = 0; ha->fw_dump_reading = 0;
ha->fw_dump = NULL; ha->fw_dump = NULL;
ha->fw_dump_len = 0; ha->fw_dump_len = 0;
ha->fw_dump_template = NULL;
ha->fw_dump_template_len = 0; for (j = 0; j < 2; j++, fwdt++) {
if (fwdt->template)
vfree(fwdt->template);
fwdt->template = NULL;
fwdt->length = 0;
}
} }
/* /*
......
...@@ -2633,6 +2633,8 @@ qla25xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, ...@@ -2633,6 +2633,8 @@ qla25xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
goto slow_read; goto slow_read;
try_fast: try_fast:
if (offset & 0xff)
goto slow_read;
optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
&optrom_dma, GFP_KERNEL); &optrom_dma, GFP_KERNEL);
if (!optrom) { if (!optrom) {
......
...@@ -38,7 +38,6 @@ qla27xx_insert32(uint32_t value, void *buf, ulong *len) ...@@ -38,7 +38,6 @@ qla27xx_insert32(uint32_t value, void *buf, ulong *len)
static inline void static inline void
qla27xx_insertbuf(void *mem, ulong size, void *buf, ulong *len) qla27xx_insertbuf(void *mem, ulong size, void *buf, ulong *len)
{ {
if (buf && mem && size) { if (buf && mem && size) {
buf += *len; buf += *len;
memcpy(buf, mem, size); memcpy(buf, mem, size);
...@@ -855,23 +854,11 @@ qla27xx_walk_template(struct scsi_qla_host *vha, ...@@ -855,23 +854,11 @@ qla27xx_walk_template(struct scsi_qla_host *vha,
if (count) if (count)
ql_dbg(ql_dbg_misc, vha, 0xd018, ql_dbg(ql_dbg_misc, vha, 0xd018,
"%s: entry residual count (%lx)\n", __func__, count); "%s: entry count residual=+%lu\n", __func__, count);
if (ent) if (ent)
ql_dbg(ql_dbg_misc, vha, 0xd019, ql_dbg(ql_dbg_misc, vha, 0xd019,
"%s: missing end entry (%lx)\n", __func__, count); "%s: missing end entry\n", __func__);
if (buf && *len != vha->hw->fw_dump_len)
ql_dbg(ql_dbg_misc, vha, 0xd01b,
"%s: length=%#lx residual=%+ld\n",
__func__, *len, vha->hw->fw_dump_len - *len);
if (buf) {
ql_log(ql_log_warn, vha, 0xd015,
"Firmware dump saved to temp buffer (%lu/%p)\n",
vha->host_no, vha->hw->fw_dump);
qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP);
}
} }
static void static void
...@@ -894,8 +881,8 @@ qla27xx_driver_info(struct qla27xx_fwdt_template *tmp) ...@@ -894,8 +881,8 @@ qla27xx_driver_info(struct qla27xx_fwdt_template *tmp)
} }
static void static void
qla27xx_firmware_info(struct qla27xx_fwdt_template *tmp, qla27xx_firmware_info(struct scsi_qla_host *vha,
struct scsi_qla_host *vha) struct qla27xx_fwdt_template *tmp)
{ {
tmp->firmware_version[0] = vha->hw->fw_major_version; tmp->firmware_version[0] = vha->hw->fw_major_version;
tmp->firmware_version[1] = vha->hw->fw_minor_version; tmp->firmware_version[1] = vha->hw->fw_minor_version;
...@@ -912,7 +899,7 @@ ql27xx_edit_template(struct scsi_qla_host *vha, ...@@ -912,7 +899,7 @@ ql27xx_edit_template(struct scsi_qla_host *vha,
{ {
qla27xx_time_stamp(tmp); qla27xx_time_stamp(tmp);
qla27xx_driver_info(tmp); qla27xx_driver_info(tmp);
qla27xx_firmware_info(tmp, vha); qla27xx_firmware_info(vha, tmp);
} }
static inline uint32_t static inline uint32_t
...@@ -943,26 +930,26 @@ qla27xx_verify_template_header(struct qla27xx_fwdt_template *tmp) ...@@ -943,26 +930,26 @@ qla27xx_verify_template_header(struct qla27xx_fwdt_template *tmp)
return le32_to_cpu(tmp->template_type) == TEMPLATE_TYPE_FWDUMP; return le32_to_cpu(tmp->template_type) == TEMPLATE_TYPE_FWDUMP;
} }
static void static ulong
qla27xx_execute_fwdt_template(struct scsi_qla_host *vha) qla27xx_execute_fwdt_template(struct scsi_qla_host *vha,
struct qla27xx_fwdt_template *tmp, void *buf)
{ {
struct qla27xx_fwdt_template *tmp = vha->hw->fw_dump_template; ulong len = 0;
ulong len;
if (qla27xx_fwdt_template_valid(tmp)) { if (qla27xx_fwdt_template_valid(tmp)) {
len = tmp->template_size; len = tmp->template_size;
tmp = memcpy(vha->hw->fw_dump, tmp, len); tmp = memcpy(buf, tmp, len);
ql27xx_edit_template(vha, tmp); ql27xx_edit_template(vha, tmp);
qla27xx_walk_template(vha, tmp, tmp, &len); qla27xx_walk_template(vha, tmp, buf, &len);
vha->hw->fw_dump_len = len;
vha->hw->fw_dumped = 1;
} }
return len;
} }
ulong ulong
qla27xx_fwdt_calculate_dump_size(struct scsi_qla_host *vha) qla27xx_fwdt_calculate_dump_size(struct scsi_qla_host *vha, void *p)
{ {
struct qla27xx_fwdt_template *tmp = vha->hw->fw_dump_template; struct qla27xx_fwdt_template *tmp = p;
ulong len = 0; ulong len = 0;
if (qla27xx_fwdt_template_valid(tmp)) { if (qla27xx_fwdt_template_valid(tmp)) {
...@@ -1012,17 +999,41 @@ qla27xx_fwdump(scsi_qla_host_t *vha, int hardware_locked) ...@@ -1012,17 +999,41 @@ qla27xx_fwdump(scsi_qla_host_t *vha, int hardware_locked)
spin_lock_irqsave(&vha->hw->hardware_lock, flags); spin_lock_irqsave(&vha->hw->hardware_lock, flags);
#endif #endif
if (!vha->hw->fw_dump) if (!vha->hw->fw_dump) {
ql_log(ql_log_warn, vha, 0xd01e, "fwdump buffer missing.\n"); ql_log(ql_log_warn, vha, 0xd01e, "-> fwdump no buffer\n");
else if (!vha->hw->fw_dump_template) } else if (vha->hw->fw_dumped) {
ql_log(ql_log_warn, vha, 0xd01f, "fwdump template missing.\n"); ql_log(ql_log_warn, vha, 0xd01f,
else if (vha->hw->fw_dumped) "-> Firmware already dumped (%p) -- ignoring request\n",
ql_log(ql_log_warn, vha, 0xd300, vha->hw->fw_dump);
"Firmware has been previously dumped (%p)," } else {
" -- ignoring request\n", vha->hw->fw_dump); struct fwdt *fwdt = vha->hw->fwdt;
else { uint j;
QLA_FW_STOPPED(vha->hw); ulong len;
qla27xx_execute_fwdt_template(vha); void *buf = vha->hw->fw_dump;
for (j = 0; j < 2; j++, fwdt++, buf += len) {
ql_log(ql_log_warn, vha, 0xd011,
"-> fwdt%u running...\n", j);
if (!fwdt->template) {
ql_log(ql_log_warn, vha, 0xd012,
"-> fwdt%u no template\n", j);
break;
}
len = qla27xx_execute_fwdt_template(vha,
fwdt->template, buf);
if (len != fwdt->dump_size) {
ql_log(ql_log_warn, vha, 0xd013,
"-> fwdt%u fwdump residual=%+ld\n",
j, fwdt->dump_size - len);
}
}
vha->hw->fw_dump_len = buf - (void *)vha->hw->fw_dump;
vha->hw->fw_dumped = 1;
ql_log(ql_log_warn, vha, 0xd015,
"-> Firmware dump saved to buffer (%lu/%p) <%lx>\n",
vha->host_no, vha->hw->fw_dump, vha->hw->fw_dump_cap_flags);
qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP);
} }
#ifndef __CHECKER__ #ifndef __CHECKER__
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment