linux/drivers/scsi/qla2xxx/qla_tmpl.c
Michael Hernandez be37aa4b99 scsi: qla2xxx: Fix system crash while triggering FW dump
This patch fixes system hang/crash while firmware dump is attempted with
Block MQ enabled in qla2xxx driver. Fix is to remove check in fw dump
template entries for existing request and response queues so that full
buffer size is calculated during template size calculation.

Following stack trace is seen during firmware dump capture process

[  694.390588] qla2xxx [0000:81:00.0]-5003:11: ISP System Error - mbx1=4b1fh mbx2=10h mbx3=2ah mbx7=0h.
[  694.402336] BUG: unable to handle kernel paging request at ffffc90008c7b000
[  694.402372] IP: memcpy_erms+0x6/0x10
[  694.402386] PGD 105f01a067
[  694.402386] PUD 85f89c067
[  694.402398] PMD 10490cb067
[  694.402409] PTE 0
[  694.402421]
[  694.402437] Oops: 0002 [#1] PREEMPT SMP
[  694.402452] Modules linked in: netconsole configfs qla2xxx scsi_transport_fc
nvme_fc nvme_fabrics bnep bluetooth rfkill xt_tcpudp unix_diag xt_multiport
ip6table_filter ip6_tables iptable_filter ip_tables x_tables af_packet
iscsi_ibft iscsi_boot_sysfs xfs libcrc32c ipmi_ssif sb_edac edac_core
x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm irqbypass igb
crct10dif_pclmul crc32_pclmul ghash_clmulni_intel pcbc aesni_intel iTCO_wdt
aes_x86_64 crypto_simd ptp iTCO_vendor_support glue_helper cryptd lpc_ich joydev
i2c_i801 pcspkr ioatdma mei_me pps_core tpm_tis mei mfd_core acpi_power_meter
tpm_tis_core ipmi_si ipmi_devintf tpm ipmi_msghandler shpchp wmi dca button
acpi_pad btrfs xor uas usb_storage hid_generic usbhid raid6_pq crc32c_intel ast
i2c_algo_bit drm_kms_helper syscopyarea sysfillrect
[  694.402692]  sysimgblt fb_sys_fops xhci_pci ttm ehci_pci sr_mod xhci_hcd
cdrom ehci_hcd drm usbcore sg
[  694.402730] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.10.0-1-default+ #19
[  694.402753] Hardware name: Supermicro X10DRi/X10DRi, BIOS 1.1a 10/16/2015
[  694.402776] task: ffffffff81c0e4c0 task.stack: ffffffff81c00000
[  694.402798] RIP: 0010:memcpy_erms+0x6/0x10
[  694.402813] RSP: 0018:ffff88085fc03cd0 EFLAGS: 00210006
[  694.402832] RAX: ffffc90008c7ae0c RBX: 0000000000000004 RCX: 000000000001fe0c
[  694.402856] RDX: 0000000000020000 RSI: ffff8810332c01f4 RDI: ffffc90008c7b000
[  694.402879] RBP: ffff88085fc03d18 R08: 0000000000020000 R09: 0000000000279e0a
[  694.402903] R10: 0000000000000000 R11: f000000000000000 R12: ffff88085fc03d80
[  694.402927] R13: ffffc90008a01000 R14: ffffc90008a056d4 R15: ffff881052ef17e0
[  694.402951] FS:  0000000000000000(0000) GS:ffff88085fc00000(0000) knlGS:0000000000000000
[  694.402977] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  694.403012] CR2: ffffc90008c7b000 CR3: 0000000001c09000 CR4: 00000000001406f0
[  694.403036] Call Trace:
[  694.403047]  <IRQ>
[  694.403072]  ? qla27xx_fwdt_entry_t263+0x18e/0x380 [qla2xxx]
[  694.403099]  qla27xx_walk_template+0x9d/0x1a0 [qla2xxx]
[  694.403124]  qla27xx_fwdump+0x1f3/0x272 [qla2xxx]
[  694.403149]  qla2x00_async_event+0xb08/0x1a50 [qla2xxx]
[  694.403169]  ? enqueue_task_fair+0xa2/0x9d0

Signed-off-by: Mike Hernandez <michael.hernandez@cavium.com>
Signed-off-by: Joe Carnuccio <joe.carnuccio@cavium.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@cavium.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2017-08-08 11:49:50 -04:00

1000 lines
27 KiB
C

/*
* QLogic Fibre Channel HBA Driver
* Copyright (c) 2003-2014 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
#include "qla_def.h"
#include "qla_tmpl.h"
/* note default template is in big endian */
static const uint32_t ql27xx_fwdt_default_template[] = {
0x63000000, 0xa4000000, 0x7c050000, 0x00000000,
0x30000000, 0x01000000, 0x00000000, 0xc0406eb4,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x04010000, 0x14000000, 0x00000000,
0x02000000, 0x44000000, 0x09010000, 0x10000000,
0x00000000, 0x02000000, 0x01010000, 0x1c000000,
0x00000000, 0x02000000, 0x00600000, 0x00000000,
0xc0000000, 0x01010000, 0x1c000000, 0x00000000,
0x02000000, 0x00600000, 0x00000000, 0xcc000000,
0x01010000, 0x1c000000, 0x00000000, 0x02000000,
0x10600000, 0x00000000, 0xd4000000, 0x01010000,
0x1c000000, 0x00000000, 0x02000000, 0x700f0000,
0x00000060, 0xf0000000, 0x00010000, 0x18000000,
0x00000000, 0x02000000, 0x00700000, 0x041000c0,
0x00010000, 0x18000000, 0x00000000, 0x02000000,
0x10700000, 0x041000c0, 0x00010000, 0x18000000,
0x00000000, 0x02000000, 0x40700000, 0x041000c0,
0x01010000, 0x1c000000, 0x00000000, 0x02000000,
0x007c0000, 0x01000000, 0xc0000000, 0x00010000,
0x18000000, 0x00000000, 0x02000000, 0x007c0000,
0x040300c4, 0x00010000, 0x18000000, 0x00000000,
0x02000000, 0x007c0000, 0x040100c0, 0x01010000,
0x1c000000, 0x00000000, 0x02000000, 0x007c0000,
0x00000000, 0xc0000000, 0x00010000, 0x18000000,
0x00000000, 0x02000000, 0x007c0000, 0x04200000,
0x0b010000, 0x18000000, 0x00000000, 0x02000000,
0x0c000000, 0x00000000, 0x02010000, 0x20000000,
0x00000000, 0x02000000, 0x700f0000, 0x040100fc,
0xf0000000, 0x000000b0, 0x02010000, 0x20000000,
0x00000000, 0x02000000, 0x700f0000, 0x040100fc,
0xf0000000, 0x000010b0, 0x02010000, 0x20000000,
0x00000000, 0x02000000, 0x700f0000, 0x040100fc,
0xf0000000, 0x000020b0, 0x02010000, 0x20000000,
0x00000000, 0x02000000, 0x700f0000, 0x040100fc,
0xf0000000, 0x000030b0, 0x02010000, 0x20000000,
0x00000000, 0x02000000, 0x700f0000, 0x040100fc,
0xf0000000, 0x000040b0, 0x02010000, 0x20000000,
0x00000000, 0x02000000, 0x700f0000, 0x040100fc,
0xf0000000, 0x000050b0, 0x02010000, 0x20000000,
0x00000000, 0x02000000, 0x700f0000, 0x040100fc,
0xf0000000, 0x000060b0, 0x02010000, 0x20000000,
0x00000000, 0x02000000, 0x700f0000, 0x040100fc,
0xf0000000, 0x000070b0, 0x02010000, 0x20000000,
0x00000000, 0x02000000, 0x700f0000, 0x040100fc,
0xf0000000, 0x000080b0, 0x02010000, 0x20000000,
0x00000000, 0x02000000, 0x700f0000, 0x040100fc,
0xf0000000, 0x000090b0, 0x02010000, 0x20000000,
0x00000000, 0x02000000, 0x700f0000, 0x040100fc,
0xf0000000, 0x0000a0b0, 0x00010000, 0x18000000,
0x00000000, 0x02000000, 0x0a000000, 0x040100c0,
0x00010000, 0x18000000, 0x00000000, 0x02000000,
0x0a000000, 0x04200080, 0x00010000, 0x18000000,
0x00000000, 0x02000000, 0x00be0000, 0x041000c0,
0x00010000, 0x18000000, 0x00000000, 0x02000000,
0x10be0000, 0x041000c0, 0x00010000, 0x18000000,
0x00000000, 0x02000000, 0x20be0000, 0x041000c0,
0x00010000, 0x18000000, 0x00000000, 0x02000000,
0x30be0000, 0x041000c0, 0x00010000, 0x18000000,
0x00000000, 0x02000000, 0x00b00000, 0x041000c0,
0x00010000, 0x18000000, 0x00000000, 0x02000000,
0x10b00000, 0x041000c0, 0x00010000, 0x18000000,
0x00000000, 0x02000000, 0x20b00000, 0x041000c0,
0x00010000, 0x18000000, 0x00000000, 0x02000000,
0x30b00000, 0x041000c0, 0x00010000, 0x18000000,
0x00000000, 0x02000000, 0x00300000, 0x041000c0,
0x00010000, 0x18000000, 0x00000000, 0x02000000,
0x10300000, 0x041000c0, 0x00010000, 0x18000000,
0x00000000, 0x02000000, 0x20300000, 0x041000c0,
0x00010000, 0x18000000, 0x00000000, 0x02000000,
0x30300000, 0x041000c0, 0x0a010000, 0x10000000,
0x00000000, 0x02000000, 0x06010000, 0x1c000000,
0x00000000, 0x02000000, 0x01000000, 0x00000200,
0xff230200, 0x06010000, 0x1c000000, 0x00000000,
0x02000000, 0x02000000, 0x00001000, 0x00000000,
0x07010000, 0x18000000, 0x00000000, 0x02000000,
0x00000000, 0x01000000, 0x07010000, 0x18000000,
0x00000000, 0x02000000, 0x00000000, 0x02000000,
0x07010000, 0x18000000, 0x00000000, 0x02000000,
0x00000000, 0x03000000, 0x0d010000, 0x14000000,
0x00000000, 0x02000000, 0x00000000, 0xff000000,
0x10000000, 0x00000000, 0x00000080,
};
static inline void __iomem *
qla27xx_isp_reg(struct scsi_qla_host *vha)
{
return &vha->hw->iobase->isp24;
}
static inline void
qla27xx_insert16(uint16_t value, void *buf, ulong *len)
{
if (buf) {
buf += *len;
*(__le16 *)buf = cpu_to_le16(value);
}
*len += sizeof(value);
}
static inline void
qla27xx_insert32(uint32_t value, void *buf, ulong *len)
{
if (buf) {
buf += *len;
*(__le32 *)buf = cpu_to_le32(value);
}
*len += sizeof(value);
}
static inline void
qla27xx_insertbuf(void *mem, ulong size, void *buf, ulong *len)
{
if (buf && mem && size) {
buf += *len;
memcpy(buf, mem, size);
}
*len += size;
}
static inline void
qla27xx_read8(void __iomem *window, void *buf, ulong *len)
{
uint8_t value = ~0;
if (buf) {
value = RD_REG_BYTE(window);
}
qla27xx_insert32(value, buf, len);
}
static inline void
qla27xx_read16(void __iomem *window, void *buf, ulong *len)
{
uint16_t value = ~0;
if (buf) {
value = RD_REG_WORD(window);
}
qla27xx_insert32(value, buf, len);
}
static inline void
qla27xx_read32(void __iomem *window, void *buf, ulong *len)
{
uint32_t value = ~0;
if (buf) {
value = RD_REG_DWORD(window);
}
qla27xx_insert32(value, buf, len);
}
static inline void (*qla27xx_read_vector(uint width))(void __iomem*, void *, ulong *)
{
return
(width == 1) ? qla27xx_read8 :
(width == 2) ? qla27xx_read16 :
qla27xx_read32;
}
static inline void
qla27xx_read_reg(__iomem struct device_reg_24xx *reg,
uint offset, void *buf, ulong *len)
{
void __iomem *window = (void __iomem *)reg + offset;
qla27xx_read32(window, buf, len);
}
static inline void
qla27xx_write_reg(__iomem struct device_reg_24xx *reg,
uint offset, uint32_t data, void *buf)
{
__iomem void *window = (void __iomem *)reg + offset;
if (buf) {
WRT_REG_DWORD(window, data);
}
}
static inline void
qla27xx_read_window(__iomem struct device_reg_24xx *reg,
uint32_t addr, uint offset, uint count, uint width, void *buf,
ulong *len)
{
void __iomem *window = (void __iomem *)reg + offset;
void (*readn)(void __iomem*, void *, ulong *) = qla27xx_read_vector(width);
qla27xx_write_reg(reg, IOBASE_ADDR, addr, buf);
while (count--) {
qla27xx_insert32(addr, buf, len);
readn(window, buf, len);
window += width;
addr++;
}
}
static inline void
qla27xx_skip_entry(struct qla27xx_fwdt_entry *ent, void *buf)
{
if (buf)
ent->hdr.driver_flags |= DRIVER_FLAG_SKIP_ENTRY;
}
static int
qla27xx_fwdt_entry_t0(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ql_dbg(ql_dbg_misc, vha, 0xd100,
"%s: nop [%lx]\n", __func__, *len);
qla27xx_skip_entry(ent, buf);
return false;
}
static int
qla27xx_fwdt_entry_t255(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ql_dbg(ql_dbg_misc, vha, 0xd1ff,
"%s: end [%lx]\n", __func__, *len);
qla27xx_skip_entry(ent, buf);
/* terminate */
return true;
}
static int
qla27xx_fwdt_entry_t256(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
ql_dbg(ql_dbg_misc, vha, 0xd200,
"%s: rdio t1 [%lx]\n", __func__, *len);
qla27xx_read_window(reg, ent->t256.base_addr, ent->t256.pci_offset,
ent->t256.reg_count, ent->t256.reg_width, buf, len);
return false;
}
static int
qla27xx_fwdt_entry_t257(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
ql_dbg(ql_dbg_misc, vha, 0xd201,
"%s: wrio t1 [%lx]\n", __func__, *len);
qla27xx_write_reg(reg, IOBASE_ADDR, ent->t257.base_addr, buf);
qla27xx_write_reg(reg, ent->t257.pci_offset, ent->t257.write_data, buf);
return false;
}
static int
qla27xx_fwdt_entry_t258(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
ql_dbg(ql_dbg_misc, vha, 0xd202,
"%s: rdio t2 [%lx]\n", __func__, *len);
qla27xx_write_reg(reg, ent->t258.banksel_offset, ent->t258.bank, buf);
qla27xx_read_window(reg, ent->t258.base_addr, ent->t258.pci_offset,
ent->t258.reg_count, ent->t258.reg_width, buf, len);
return false;
}
static int
qla27xx_fwdt_entry_t259(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
ql_dbg(ql_dbg_misc, vha, 0xd203,
"%s: wrio t2 [%lx]\n", __func__, *len);
qla27xx_write_reg(reg, IOBASE_ADDR, ent->t259.base_addr, buf);
qla27xx_write_reg(reg, ent->t259.banksel_offset, ent->t259.bank, buf);
qla27xx_write_reg(reg, ent->t259.pci_offset, ent->t259.write_data, buf);
return false;
}
static int
qla27xx_fwdt_entry_t260(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
ql_dbg(ql_dbg_misc, vha, 0xd204,
"%s: rdpci [%lx]\n", __func__, *len);
qla27xx_insert32(ent->t260.pci_offset, buf, len);
qla27xx_read_reg(reg, ent->t260.pci_offset, buf, len);
return false;
}
static int
qla27xx_fwdt_entry_t261(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
ql_dbg(ql_dbg_misc, vha, 0xd205,
"%s: wrpci [%lx]\n", __func__, *len);
qla27xx_write_reg(reg, ent->t261.pci_offset, ent->t261.write_data, buf);
return false;
}
static int
qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ulong dwords;
ulong start;
ulong end;
ql_dbg(ql_dbg_misc, vha, 0xd206,
"%s: rdram(%x) [%lx]\n", __func__, ent->t262.ram_area, *len);
start = ent->t262.start_addr;
end = ent->t262.end_addr;
if (ent->t262.ram_area == T262_RAM_AREA_CRITICAL_RAM) {
;
} else if (ent->t262.ram_area == T262_RAM_AREA_EXTERNAL_RAM) {
end = vha->hw->fw_memory_size;
if (buf)
ent->t262.end_addr = end;
} else if (ent->t262.ram_area == T262_RAM_AREA_SHARED_RAM) {
start = vha->hw->fw_shared_ram_start;
end = vha->hw->fw_shared_ram_end;
if (buf) {
ent->t262.start_addr = start;
ent->t262.end_addr = end;
}
} else if (ent->t262.ram_area == T262_RAM_AREA_DDR_RAM) {
start = vha->hw->fw_ddr_ram_start;
end = vha->hw->fw_ddr_ram_end;
if (buf) {
ent->t262.start_addr = start;
ent->t262.end_addr = end;
}
} else {
ql_dbg(ql_dbg_misc, vha, 0xd022,
"%s: unknown area %x\n", __func__, ent->t262.ram_area);
qla27xx_skip_entry(ent, buf);
goto done;
}
if (end < start || start == 0 || end == 0) {
ql_dbg(ql_dbg_misc, vha, 0xd023,
"%s: unusable range (start=%x end=%x)\n", __func__,
ent->t262.end_addr, ent->t262.start_addr);
qla27xx_skip_entry(ent, buf);
goto done;
}
dwords = end - start + 1;
if (buf) {
buf += *len;
qla24xx_dump_ram(vha->hw, start, buf, dwords, &buf);
}
*len += dwords * sizeof(uint32_t);
done:
return false;
}
static int
qla27xx_fwdt_entry_t263(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
uint count = 0;
uint i;
uint length;
ql_dbg(ql_dbg_misc, vha, 0xd207,
"%s: getq(%x) [%lx]\n", __func__, ent->t263.queue_type, *len);
if (ent->t263.queue_type == T263_QUEUE_TYPE_REQ) {
for (i = 0; i < vha->hw->max_req_queues; i++) {
struct req_que *req = vha->hw->req_q_map[i];
if (req || !buf) {
length = req ?
req->length : REQUEST_ENTRY_CNT_24XX;
qla27xx_insert16(i, buf, len);
qla27xx_insert16(length, buf, len);
qla27xx_insertbuf(req ? req->ring : NULL,
length * sizeof(*req->ring), buf, len);
count++;
}
}
} else if (ent->t263.queue_type == T263_QUEUE_TYPE_RSP) {
for (i = 0; i < vha->hw->max_rsp_queues; i++) {
struct rsp_que *rsp = vha->hw->rsp_q_map[i];
if (rsp || !buf) {
length = rsp ?
rsp->length : RESPONSE_ENTRY_CNT_MQ;
qla27xx_insert16(i, buf, len);
qla27xx_insert16(length, buf, len);
qla27xx_insertbuf(rsp ? rsp->ring : NULL,
length * sizeof(*rsp->ring), buf, len);
count++;
}
}
} else if (QLA_TGT_MODE_ENABLED() &&
ent->t263.queue_type == T263_QUEUE_TYPE_ATIO) {
struct qla_hw_data *ha = vha->hw;
struct atio *atr = ha->tgt.atio_ring;
if (atr || !buf) {
length = ha->tgt.atio_q_length;
qla27xx_insert16(0, buf, len);
qla27xx_insert16(length, buf, len);
qla27xx_insertbuf(atr, length * sizeof(*atr), buf, len);
count++;
}
} else {
ql_dbg(ql_dbg_misc, vha, 0xd026,
"%s: unknown queue %x\n", __func__, ent->t263.queue_type);
qla27xx_skip_entry(ent, buf);
}
if (buf)
ent->t263.num_queues = count;
return false;
}
static int
qla27xx_fwdt_entry_t264(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ql_dbg(ql_dbg_misc, vha, 0xd208,
"%s: getfce [%lx]\n", __func__, *len);
if (vha->hw->fce) {
if (buf) {
ent->t264.fce_trace_size = FCE_SIZE;
ent->t264.write_pointer = vha->hw->fce_wr;
ent->t264.base_pointer = vha->hw->fce_dma;
ent->t264.fce_enable_mb0 = vha->hw->fce_mb[0];
ent->t264.fce_enable_mb2 = vha->hw->fce_mb[2];
ent->t264.fce_enable_mb3 = vha->hw->fce_mb[3];
ent->t264.fce_enable_mb4 = vha->hw->fce_mb[4];
ent->t264.fce_enable_mb5 = vha->hw->fce_mb[5];
ent->t264.fce_enable_mb6 = vha->hw->fce_mb[6];
}
qla27xx_insertbuf(vha->hw->fce, FCE_SIZE, buf, len);
} else {
ql_dbg(ql_dbg_misc, vha, 0xd027,
"%s: missing fce\n", __func__);
qla27xx_skip_entry(ent, buf);
}
return false;
}
static int
qla27xx_fwdt_entry_t265(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
ql_dbg(ql_dbg_misc, vha, 0xd209,
"%s: pause risc [%lx]\n", __func__, *len);
if (buf)
qla24xx_pause_risc(reg, vha->hw);
return false;
}
static int
qla27xx_fwdt_entry_t266(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ql_dbg(ql_dbg_misc, vha, 0xd20a,
"%s: reset risc [%lx]\n", __func__, *len);
if (buf)
qla24xx_soft_reset(vha->hw);
return false;
}
static int
qla27xx_fwdt_entry_t267(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
ql_dbg(ql_dbg_misc, vha, 0xd20b,
"%s: dis intr [%lx]\n", __func__, *len);
qla27xx_write_reg(reg, ent->t267.pci_offset, ent->t267.data, buf);
return false;
}
static int
qla27xx_fwdt_entry_t268(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ql_dbg(ql_dbg_misc, vha, 0xd20c,
"%s: gethb(%x) [%lx]\n", __func__, ent->t268.buf_type, *len);
if (ent->t268.buf_type == T268_BUF_TYPE_EXTD_TRACE) {
if (vha->hw->eft) {
if (buf) {
ent->t268.buf_size = EFT_SIZE;
ent->t268.start_addr = vha->hw->eft_dma;
}
qla27xx_insertbuf(vha->hw->eft, EFT_SIZE, buf, len);
} else {
ql_dbg(ql_dbg_misc, vha, 0xd028,
"%s: missing eft\n", __func__);
qla27xx_skip_entry(ent, buf);
}
} else {
ql_dbg(ql_dbg_misc, vha, 0xd02b,
"%s: unknown buffer %x\n", __func__, ent->t268.buf_type);
qla27xx_skip_entry(ent, buf);
}
return false;
}
static int
qla27xx_fwdt_entry_t269(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ql_dbg(ql_dbg_misc, vha, 0xd20d,
"%s: scratch [%lx]\n", __func__, *len);
qla27xx_insert32(0xaaaaaaaa, buf, len);
qla27xx_insert32(0xbbbbbbbb, buf, len);
qla27xx_insert32(0xcccccccc, buf, len);
qla27xx_insert32(0xdddddddd, buf, len);
qla27xx_insert32(*len + sizeof(uint32_t), buf, len);
if (buf)
ent->t269.scratch_size = 5 * sizeof(uint32_t);
return false;
}
static int
qla27xx_fwdt_entry_t270(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
ulong dwords = ent->t270.count;
ulong addr = ent->t270.addr;
ql_dbg(ql_dbg_misc, vha, 0xd20e,
"%s: rdremreg [%lx]\n", __func__, *len);
qla27xx_write_reg(reg, IOBASE_ADDR, 0x40, buf);
while (dwords--) {
qla27xx_write_reg(reg, 0xc0, addr|0x80000000, buf);
qla27xx_insert32(addr, buf, len);
qla27xx_read_reg(reg, 0xc4, buf, len);
addr += sizeof(uint32_t);
}
return false;
}
static int
qla27xx_fwdt_entry_t271(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
ulong addr = ent->t271.addr;
ulong data = ent->t271.data;
ql_dbg(ql_dbg_misc, vha, 0xd20f,
"%s: wrremreg [%lx]\n", __func__, *len);
qla27xx_write_reg(reg, IOBASE_ADDR, 0x40, buf);
qla27xx_write_reg(reg, 0xc4, data, buf);
qla27xx_write_reg(reg, 0xc0, addr, buf);
return false;
}
static int
qla27xx_fwdt_entry_t272(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ulong dwords = ent->t272.count;
ulong start = ent->t272.addr;
ql_dbg(ql_dbg_misc, vha, 0xd210,
"%s: rdremram [%lx]\n", __func__, *len);
if (buf) {
ql_dbg(ql_dbg_misc, vha, 0xd02c,
"%s: @%lx -> (%lx dwords)\n", __func__, start, dwords);
buf += *len;
qla27xx_dump_mpi_ram(vha->hw, start, buf, dwords, &buf);
}
*len += dwords * sizeof(uint32_t);
return false;
}
static int
qla27xx_fwdt_entry_t273(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ulong dwords = ent->t273.count;
ulong addr = ent->t273.addr;
uint32_t value;
ql_dbg(ql_dbg_misc, vha, 0xd211,
"%s: pcicfg [%lx]\n", __func__, *len);
while (dwords--) {
value = ~0;
if (pci_read_config_dword(vha->hw->pdev, addr, &value))
ql_dbg(ql_dbg_misc, vha, 0xd02d,
"%s: failed pcicfg read at %lx\n", __func__, addr);
qla27xx_insert32(addr, buf, len);
qla27xx_insert32(value, buf, len);
addr += sizeof(uint32_t);
}
return false;
}
static int
qla27xx_fwdt_entry_t274(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
uint count = 0;
uint i;
ql_dbg(ql_dbg_misc, vha, 0xd212,
"%s: getqsh(%x) [%lx]\n", __func__, ent->t274.queue_type, *len);
if (ent->t274.queue_type == T274_QUEUE_TYPE_REQ_SHAD) {
for (i = 0; i < vha->hw->max_req_queues; i++) {
struct req_que *req = vha->hw->req_q_map[i];
if (req || !buf) {
qla27xx_insert16(i, buf, len);
qla27xx_insert16(1, buf, len);
qla27xx_insert32(req && req->out_ptr ?
*req->out_ptr : 0, buf, len);
count++;
}
}
} else if (ent->t274.queue_type == T274_QUEUE_TYPE_RSP_SHAD) {
for (i = 0; i < vha->hw->max_rsp_queues; i++) {
struct rsp_que *rsp = vha->hw->rsp_q_map[i];
if (rsp || !buf) {
qla27xx_insert16(i, buf, len);
qla27xx_insert16(1, buf, len);
qla27xx_insert32(rsp && rsp->in_ptr ?
*rsp->in_ptr : 0, buf, len);
count++;
}
}
} else if (QLA_TGT_MODE_ENABLED() &&
ent->t274.queue_type == T274_QUEUE_TYPE_ATIO_SHAD) {
struct qla_hw_data *ha = vha->hw;
struct atio *atr = ha->tgt.atio_ring_ptr;
if (atr || !buf) {
qla27xx_insert16(0, buf, len);
qla27xx_insert16(1, buf, len);
qla27xx_insert32(ha->tgt.atio_q_in ?
readl(ha->tgt.atio_q_in) : 0, buf, len);
count++;
}
} else {
ql_dbg(ql_dbg_misc, vha, 0xd02f,
"%s: unknown queue %x\n", __func__, ent->t274.queue_type);
qla27xx_skip_entry(ent, buf);
}
if (buf)
ent->t274.num_queues = count;
if (!count)
qla27xx_skip_entry(ent, buf);
return false;
}
static int
qla27xx_fwdt_entry_t275(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ulong offset = offsetof(typeof(*ent), t275.buffer);
ql_dbg(ql_dbg_misc, vha, 0xd213,
"%s: buffer(%x) [%lx]\n", __func__, ent->t275.length, *len);
if (!ent->t275.length) {
ql_dbg(ql_dbg_misc, vha, 0xd020,
"%s: buffer zero length\n", __func__);
qla27xx_skip_entry(ent, buf);
goto done;
}
if (offset + ent->t275.length > ent->hdr.entry_size) {
ql_dbg(ql_dbg_misc, vha, 0xd030,
"%s: buffer overflow\n", __func__);
qla27xx_skip_entry(ent, buf);
goto done;
}
qla27xx_insertbuf(ent->t275.buffer, ent->t275.length, buf, len);
done:
return false;
}
static int
qla27xx_fwdt_entry_other(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ql_dbg(ql_dbg_misc, vha, 0xd2ff,
"%s: type %x [%lx]\n", __func__, ent->hdr.entry_type, *len);
qla27xx_skip_entry(ent, buf);
return false;
}
struct qla27xx_fwdt_entry_call {
uint type;
int (*call)(
struct scsi_qla_host *,
struct qla27xx_fwdt_entry *,
void *,
ulong *);
};
static struct qla27xx_fwdt_entry_call ql27xx_fwdt_entry_call_list[] = {
{ ENTRY_TYPE_NOP , qla27xx_fwdt_entry_t0 } ,
{ ENTRY_TYPE_TMP_END , qla27xx_fwdt_entry_t255 } ,
{ ENTRY_TYPE_RD_IOB_T1 , qla27xx_fwdt_entry_t256 } ,
{ ENTRY_TYPE_WR_IOB_T1 , qla27xx_fwdt_entry_t257 } ,
{ ENTRY_TYPE_RD_IOB_T2 , qla27xx_fwdt_entry_t258 } ,
{ ENTRY_TYPE_WR_IOB_T2 , qla27xx_fwdt_entry_t259 } ,
{ ENTRY_TYPE_RD_PCI , qla27xx_fwdt_entry_t260 } ,
{ ENTRY_TYPE_WR_PCI , qla27xx_fwdt_entry_t261 } ,
{ ENTRY_TYPE_RD_RAM , qla27xx_fwdt_entry_t262 } ,
{ ENTRY_TYPE_GET_QUEUE , qla27xx_fwdt_entry_t263 } ,
{ ENTRY_TYPE_GET_FCE , qla27xx_fwdt_entry_t264 } ,
{ ENTRY_TYPE_PSE_RISC , qla27xx_fwdt_entry_t265 } ,
{ ENTRY_TYPE_RST_RISC , qla27xx_fwdt_entry_t266 } ,
{ ENTRY_TYPE_DIS_INTR , qla27xx_fwdt_entry_t267 } ,
{ ENTRY_TYPE_GET_HBUF , qla27xx_fwdt_entry_t268 } ,
{ ENTRY_TYPE_SCRATCH , qla27xx_fwdt_entry_t269 } ,
{ ENTRY_TYPE_RDREMREG , qla27xx_fwdt_entry_t270 } ,
{ ENTRY_TYPE_WRREMREG , qla27xx_fwdt_entry_t271 } ,
{ ENTRY_TYPE_RDREMRAM , qla27xx_fwdt_entry_t272 } ,
{ ENTRY_TYPE_PCICFG , qla27xx_fwdt_entry_t273 } ,
{ ENTRY_TYPE_GET_SHADOW , qla27xx_fwdt_entry_t274 } ,
{ ENTRY_TYPE_WRITE_BUF , qla27xx_fwdt_entry_t275 } ,
{ -1 , qla27xx_fwdt_entry_other }
};
static inline int (*qla27xx_find_entry(uint type))
(struct scsi_qla_host *, struct qla27xx_fwdt_entry *, void *, ulong *)
{
struct qla27xx_fwdt_entry_call *list = ql27xx_fwdt_entry_call_list;
while (list->type < type)
list++;
if (list->type == type)
return list->call;
return qla27xx_fwdt_entry_other;
}
static inline void *
qla27xx_next_entry(void *p)
{
struct qla27xx_fwdt_entry *ent = p;
return p + ent->hdr.entry_size;
}
static void
qla27xx_walk_template(struct scsi_qla_host *vha,
struct qla27xx_fwdt_template *tmp, void *buf, ulong *len)
{
struct qla27xx_fwdt_entry *ent = (void *)tmp + tmp->entry_offset;
ulong count = tmp->entry_count;
ql_dbg(ql_dbg_misc, vha, 0xd01a,
"%s: entry count %lx\n", __func__, count);
while (count--) {
if (buf && *len >= vha->hw->fw_dump_len)
break;
if (qla27xx_find_entry(ent->hdr.entry_type)(vha, ent, buf, len))
break;
ent = qla27xx_next_entry(ent);
}
if (count)
ql_dbg(ql_dbg_misc, vha, 0xd018,
"%s: entry residual count (%lx)\n", __func__, count);
if (ent->hdr.entry_type != ENTRY_TYPE_TMP_END)
ql_dbg(ql_dbg_misc, vha, 0xd019,
"%s: missing end entry (%lx)\n", __func__, count);
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
qla27xx_time_stamp(struct qla27xx_fwdt_template *tmp)
{
tmp->capture_timestamp = jiffies;
}
static void
qla27xx_driver_info(struct qla27xx_fwdt_template *tmp)
{
uint8_t v[] = { 0, 0, 0, 0, 0, 0 };
sscanf(qla2x00_version_str, "%hhu.%hhu.%hhu.%hhu.%hhu.%hhu",
v+0, v+1, v+2, v+3, v+4, v+5);
tmp->driver_info[0] = v[3] << 24 | v[2] << 16 | v[1] << 8 | v[0];
tmp->driver_info[1] = v[5] << 8 | v[4];
tmp->driver_info[2] = 0x12345678;
}
static void
qla27xx_firmware_info(struct qla27xx_fwdt_template *tmp,
struct scsi_qla_host *vha)
{
tmp->firmware_version[0] = vha->hw->fw_major_version;
tmp->firmware_version[1] = vha->hw->fw_minor_version;
tmp->firmware_version[2] = vha->hw->fw_subminor_version;
tmp->firmware_version[3] =
vha->hw->fw_attributes_h << 16 | vha->hw->fw_attributes;
tmp->firmware_version[4] =
vha->hw->fw_attributes_ext[1] << 16 | vha->hw->fw_attributes_ext[0];
}
static void
ql27xx_edit_template(struct scsi_qla_host *vha,
struct qla27xx_fwdt_template *tmp)
{
qla27xx_time_stamp(tmp);
qla27xx_driver_info(tmp);
qla27xx_firmware_info(tmp, vha);
}
static inline uint32_t
qla27xx_template_checksum(void *p, ulong size)
{
uint32_t *buf = p;
uint64_t sum = 0;
size /= sizeof(*buf);
while (size--)
sum += *buf++;
sum = (sum & 0xffffffff) + (sum >> 32);
return ~sum;
}
static inline int
qla27xx_verify_template_checksum(struct qla27xx_fwdt_template *tmp)
{
return qla27xx_template_checksum(tmp, tmp->template_size) == 0;
}
static inline int
qla27xx_verify_template_header(struct qla27xx_fwdt_template *tmp)
{
return tmp->template_type == TEMPLATE_TYPE_FWDUMP;
}
static void
qla27xx_execute_fwdt_template(struct scsi_qla_host *vha)
{
struct qla27xx_fwdt_template *tmp = vha->hw->fw_dump_template;
ulong len;
if (qla27xx_fwdt_template_valid(tmp)) {
len = tmp->template_size;
tmp = memcpy(vha->hw->fw_dump, tmp, len);
ql27xx_edit_template(vha, tmp);
qla27xx_walk_template(vha, tmp, tmp, &len);
vha->hw->fw_dump_len = len;
vha->hw->fw_dumped = 1;
}
}
ulong
qla27xx_fwdt_calculate_dump_size(struct scsi_qla_host *vha)
{
struct qla27xx_fwdt_template *tmp = vha->hw->fw_dump_template;
ulong len = 0;
if (qla27xx_fwdt_template_valid(tmp)) {
len = tmp->template_size;
qla27xx_walk_template(vha, tmp, NULL, &len);
}
return len;
}
ulong
qla27xx_fwdt_template_size(void *p)
{
struct qla27xx_fwdt_template *tmp = p;
return tmp->template_size;
}
ulong
qla27xx_fwdt_template_default_size(void)
{
return sizeof(ql27xx_fwdt_default_template);
}
const void *
qla27xx_fwdt_template_default(void)
{
return ql27xx_fwdt_default_template;
}
int
qla27xx_fwdt_template_valid(void *p)
{
struct qla27xx_fwdt_template *tmp = p;
if (!qla27xx_verify_template_header(tmp)) {
ql_log(ql_log_warn, NULL, 0xd01c,
"%s: template type %x\n", __func__, tmp->template_type);
return false;
}
if (!qla27xx_verify_template_checksum(tmp)) {
ql_log(ql_log_warn, NULL, 0xd01d,
"%s: failed template checksum\n", __func__);
return false;
}
return true;
}
void
qla27xx_fwdump(scsi_qla_host_t *vha, int hardware_locked)
{
ulong flags = 0;
#ifndef __CHECKER__
if (!hardware_locked)
spin_lock_irqsave(&vha->hw->hardware_lock, flags);
#endif
if (!vha->hw->fw_dump)
ql_log(ql_log_warn, vha, 0xd01e, "fwdump buffer missing.\n");
else if (!vha->hw->fw_dump_template)
ql_log(ql_log_warn, vha, 0xd01f, "fwdump template missing.\n");
else if (vha->hw->fw_dumped)
ql_log(ql_log_warn, vha, 0xd300,
"Firmware has been previously dumped (%p),"
" -- ignoring request\n", vha->hw->fw_dump);
else
qla27xx_execute_fwdt_template(vha);
#ifndef __CHECKER__
if (!hardware_locked)
spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
#endif
}