summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/qla2xxx/qla_sup.c
diff options
context:
space:
mode:
authorMichael Hernandez2019-03-12 19:08:22 +0100
committerMartin K. Petersen2019-03-19 17:22:55 +0100
commit3f006ac342c033c795aa0ec2d0dde63975e2144b (patch)
treebfb5a734e42ff536fba00e78f0c0d72b3f5a36ce /drivers/scsi/qla2xxx/qla_sup.c
parentscsi: qla2xxx: Add 28xx flash primary/secondary status/image mechanism (diff)
downloadkernel-qcow2-linux-3f006ac342c033c795aa0ec2d0dde63975e2144b.tar.gz
kernel-qcow2-linux-3f006ac342c033c795aa0ec2d0dde63975e2144b.tar.xz
kernel-qcow2-linux-3f006ac342c033c795aa0ec2d0dde63975e2144b.zip
scsi: qla2xxx: Secure flash update support for ISP28XX
This patch adds support for Secure flash update with ISP28xx. Signed-off-by: Michael Hernandez <mhernandez@marvell.com> Signed-off-by: Himanshu Madhani <hmadhani@marvell.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_sup.c')
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c387
1 files changed, 359 insertions, 28 deletions
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index 08c7dca07c90..320c25f3a79a 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -634,7 +634,7 @@ end:
static void
qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
{
- const char *loc, *locations[] = { "DEF", "FLT" };
+ const char *locations[] = { "DEF", "FLT" }, *loc = locations[1];
const uint32_t def_fw[] =
{ FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR_81 };
const uint32_t def_boot[] =
@@ -664,20 +664,13 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
const uint32_t fcp_prio_cfg1[] =
{ FA_FCP_PRIO1_ADDR, FA_FCP_PRIO1_ADDR_25,
0 };
- uint32_t def;
- uint16_t *wptr;
- uint16_t cnt, chksum;
- uint32_t start;
- struct qla_flt_header *flt;
- struct qla_flt_region *region;
- struct qla_hw_data *ha = vha->hw;
- struct req_que *req = ha->req_q_map[0];
- def = 0;
- if (IS_QLA25XX(ha))
- def = 1;
- else if (IS_QLA81XX(ha))
- def = 2;
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t def = IS_QLA81XX(ha) ? 2 : IS_QLA25XX(ha) ? 1 : 0;
+ struct qla_flt_header *flt = (void *)ha->flt;
+ struct qla_flt_region *region = (void *)&flt[1];
+ uint16_t *wptr, cnt, chksum;
+ uint32_t start;
/* Assign FCP prio region since older adapters may not have FLT, or
FCP prio region in it's FLT.
@@ -686,12 +679,11 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
fcp_prio_cfg0[def] : fcp_prio_cfg1[def];
ha->flt_region_flt = flt_addr;
- wptr = (uint16_t *)req->ring;
- flt = (struct qla_flt_header *)req->ring;
- region = (struct qla_flt_region *)&flt[1];
- ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring,
- flt_addr << 2, OPTROM_BURST_SIZE);
- if (*wptr == cpu_to_le16(0xffff))
+ wptr = (uint16_t *)ha->flt;
+ qla24xx_read_flash_data(vha, (void *)flt, flt_addr,
+ (sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE) >> 2);
+
+ if (le16_to_cpu(*wptr) == 0xffff)
goto no_flash_data;
if (flt->version != cpu_to_le16(1)) {
ql_log(ql_log_warn, vha, 0x0047,
@@ -701,7 +693,7 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
goto no_flash_data;
}
- cnt = (sizeof(struct qla_flt_header) + le16_to_cpu(flt->length)) >> 1;
+ cnt = (sizeof(*flt) + le16_to_cpu(flt->length)) / sizeof(*wptr);
for (chksum = 0; cnt--; wptr++)
chksum += le16_to_cpu(*wptr);
if (chksum) {
@@ -712,16 +704,18 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
goto no_flash_data;
}
- loc = locations[1];
- cnt = le16_to_cpu(flt->length) / sizeof(struct qla_flt_region);
+ cnt = le16_to_cpu(flt->length) / sizeof(*region);
for ( ; cnt; cnt--, region++) {
/* Store addresses as DWORD offsets. */
start = le32_to_cpu(region->start) >> 2;
ql_dbg(ql_dbg_init, vha, 0x0049,
"FLT[%#x]: start=%#x end=%#x size=%#x.\n",
- le16_to_cpu(region->code),
- start, le32_to_cpu(region->end) >> 2,
- le32_to_cpu(region->size));
+ le16_to_cpu(region->code), start,
+ le32_to_cpu(region->end) >> 2,
+ le32_to_cpu(region->size) >> 2);
+ if (region->attribute)
+ ql_log(ql_dbg_init, vha, 0xffff,
+ "Region %x is secure\n", region->code);
switch (le16_to_cpu(region->code)) {
case FLT_REG_FCOE_FW:
@@ -2623,6 +2617,338 @@ qla24xx_read_optrom_data(struct scsi_qla_host *vha, void *buf,
return buf;
}
+static int
+qla28xx_extract_sfub_and_verify(struct scsi_qla_host *vha, uint32_t *buf,
+ uint32_t len, uint32_t buf_size_without_sfub, uint8_t *sfub_buf)
+{
+ uint32_t *p, check_sum = 0;
+ int i;
+
+ p = buf + buf_size_without_sfub;
+
+ /* Extract SFUB from end of file */
+ memcpy(sfub_buf, (uint8_t *)p,
+ sizeof(struct secure_flash_update_block));
+
+ for (i = 0; i < (sizeof(struct secure_flash_update_block) >> 2); i++)
+ check_sum += p[i];
+
+ check_sum = (~check_sum) + 1;
+
+ if (check_sum != p[i]) {
+ ql_log(ql_log_warn, vha, 0x7097,
+ "SFUB checksum failed, 0x%x, 0x%x\n",
+ check_sum, p[i]);
+ return QLA_COMMAND_ERROR;
+ }
+
+ return QLA_SUCCESS;
+}
+
+static int
+qla28xx_get_flash_region(struct scsi_qla_host *vha, uint32_t start,
+ struct qla_flt_region *region)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct qla_flt_header *flt;
+ struct qla_flt_region *flt_reg;
+ uint16_t cnt;
+ int rval = QLA_FUNCTION_FAILED;
+
+ if (!ha->flt)
+ return QLA_FUNCTION_FAILED;
+
+ flt = (struct qla_flt_header *)ha->flt;
+ flt_reg = (struct qla_flt_region *)&flt[1];
+ cnt = le16_to_cpu(flt->length) / sizeof(struct qla_flt_region);
+
+ for (; cnt; cnt--, flt_reg++) {
+ if (flt_reg->start == start) {
+ memcpy((uint8_t *)region, flt_reg,
+ sizeof(struct qla_flt_region));
+ rval = QLA_SUCCESS;
+ break;
+ }
+ }
+
+ return rval;
+}
+
+static int
+qla28xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
+ uint32_t dwords)
+{
+ struct qla_hw_data *ha = vha->hw;
+ ulong liter;
+ ulong dburst = OPTROM_BURST_DWORDS; /* burst size in dwords */
+ uint32_t sec_mask, rest_addr, fdata;
+ void *optrom = NULL;
+ dma_addr_t optrom_dma;
+ int rval;
+ struct secure_flash_update_block *sfub;
+ dma_addr_t sfub_dma;
+ uint32_t offset = faddr << 2;
+ uint32_t buf_size_without_sfub = 0;
+ struct qla_flt_region region;
+ bool reset_to_rom = false;
+ uint32_t risc_size, risc_attr = 0;
+ uint32_t *fw_array = NULL;
+
+ /* Retrieve region info - must be a start address passed in */
+ rval = qla28xx_get_flash_region(vha, offset, &region);
+
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0xffff,
+ "Invalid address %x - not a region start address\n",
+ offset);
+ goto done;
+ }
+
+ /* Allocate dma buffer for burst write */
+ optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+ &optrom_dma, GFP_KERNEL);
+ if (!optrom) {
+ ql_log(ql_log_warn, vha, 0x7095,
+ "Failed allocate burst (%x bytes)\n", OPTROM_BURST_SIZE);
+ rval = QLA_COMMAND_ERROR;
+ goto done;
+ }
+
+ /*
+ * If adapter supports secure flash and region is secure
+ * extract secure flash update block (SFUB) and verify
+ */
+ if (ha->flags.secure_adapter && region.attribute) {
+
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+ "Region %x is secure\n", region.code);
+
+ if (region.code == FLT_REG_FW ||
+ region.code == FLT_REG_FW_SEC_27XX) {
+ fw_array = dwptr;
+
+ /* 1st fw array */
+ risc_size = be32_to_cpu(fw_array[3]);
+ risc_attr = be32_to_cpu(fw_array[9]);
+
+ buf_size_without_sfub = risc_size;
+ fw_array += risc_size;
+
+ /* 2nd fw array */
+ risc_size = be32_to_cpu(fw_array[3]);
+
+ buf_size_without_sfub += risc_size;
+ fw_array += risc_size;
+
+ /* 1st dump template */
+ risc_size = be32_to_cpu(fw_array[2]);
+
+ /* skip header and ignore checksum */
+ buf_size_without_sfub += risc_size;
+ fw_array += risc_size;
+
+ if (risc_attr & BIT_9) {
+ /* 2nd dump template */
+ risc_size = be32_to_cpu(fw_array[2]);
+
+ /* skip header and ignore checksum */
+ buf_size_without_sfub += risc_size;
+ fw_array += risc_size;
+ }
+ } else {
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+ "Secure region %x not supported\n",
+ region.code);
+ rval = QLA_COMMAND_ERROR;
+ goto done;
+ }
+
+ sfub = dma_alloc_coherent(&ha->pdev->dev,
+ sizeof(struct secure_flash_update_block), &sfub_dma,
+ GFP_KERNEL);
+ if (!sfub) {
+ ql_log(ql_log_warn, vha, 0xffff,
+ "Unable to allocate memory for SFUB\n");
+ rval = QLA_COMMAND_ERROR;
+ goto done;
+ }
+
+ rval = qla28xx_extract_sfub_and_verify(vha, dwptr, dwords,
+ buf_size_without_sfub, (uint8_t *)sfub);
+
+ if (rval != QLA_SUCCESS)
+ goto done;
+
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+ "SFUB extract and verify successful\n");
+ }
+
+ rest_addr = (ha->fdt_block_size >> 2) - 1;
+ sec_mask = ~rest_addr;
+
+ /* Lock semaphore */
+ rval = qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_LOCK);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0xffff,
+ "Unable to lock flash semaphore.");
+ goto done;
+ }
+
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
+ "Unprotect flash...\n");
+ rval = qla24xx_unprotect_flash(vha);
+ if (rval) {
+ qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_UNLOCK);
+ ql_log(ql_log_warn, vha, 0x7096, "Failed unprotect flash\n");
+ goto done;
+ }
+
+ for (liter = 0; liter < dwords; liter++, faddr++) {
+ fdata = (faddr & sec_mask) << 2;
+
+ /* If start of sector */
+ if (!(faddr & rest_addr)) {
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
+ "Erase sector %#x...\n", faddr);
+ rval = qla24xx_erase_sector(vha, fdata);
+ if (rval) {
+ ql_dbg(ql_dbg_user, vha, 0x7007,
+ "Failed erase sector %#x\n", faddr);
+ goto write_protect;
+ }
+ }
+ }
+
+ if (ha->flags.secure_adapter) {
+ /*
+ * If adapter supports secure flash but FW doesn't,
+ * disable write protect, release semaphore and reset
+ * chip to execute ROM code in order to update region securely
+ */
+ if (!ha->flags.secure_fw) {
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+ "Disable Write and Release Semaphore.");
+ rval = qla24xx_protect_flash(vha);
+ if (rval != QLA_SUCCESS) {
+ qla81xx_fac_semaphore_access(vha,
+ FAC_SEMAPHORE_UNLOCK);
+ ql_log(ql_log_warn, vha, 0xffff,
+ "Unable to protect flash.");
+ goto done;
+ }
+
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+ "Reset chip to ROM.");
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ set_bit(ISP_ABORT_TO_ROM, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ rval = qla2x00_wait_for_chip_reset(vha);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0xffff,
+ "Unable to reset to ROM code.");
+ goto done;
+ }
+ reset_to_rom = true;
+ ha->flags.fac_supported = 0;
+
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+ "Lock Semaphore");
+ rval = qla2xxx_write_remote_register(vha,
+ FLASH_SEMAPHORE_REGISTER_ADDR, 0x00020002);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0xffff,
+ "Unable to lock flash semaphore.");
+ goto done;
+ }
+
+ /* Unprotect flash */
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+ "Enable Write.");
+ rval = qla2x00_write_ram_word(vha, 0x7ffd0101, 0);
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x7096,
+ "Failed unprotect flash\n");
+ goto done;
+ }
+ }
+
+ /* If region is secure, send Secure Flash MB Cmd */
+ if (region.attribute && buf_size_without_sfub) {
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+ "Sending Secure Flash MB Cmd\n");
+ rval = qla28xx_secure_flash_update(vha, 0, region.code,
+ buf_size_without_sfub, sfub_dma,
+ sizeof(struct secure_flash_update_block));
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0xffff,
+ "Secure Flash MB Cmd failed %x.", rval);
+ goto write_protect;
+ }
+ }
+
+ }
+
+ /* re-init flash offset */
+ faddr = offset >> 2;
+
+ for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) {
+ fdata = (faddr & sec_mask) << 2;
+
+ /* If smaller than a burst remaining */
+ if (dwords - liter < dburst)
+ dburst = dwords - liter;
+
+ /* Copy to dma buffer */
+ memcpy(optrom, dwptr, dburst << 2);
+
+ /* Burst write */
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
+ "Write burst (%#lx dwords)...\n", dburst);
+ rval = qla2x00_load_ram(vha, optrom_dma,
+ flash_data_addr(ha, faddr), dburst);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x7097,
+ "Failed burst write at %x (%p/%#llx)...\n",
+ flash_data_addr(ha, faddr), optrom,
+ (u64)optrom_dma);
+ break;
+ }
+
+ liter += dburst - 1;
+ faddr += dburst - 1;
+ dwptr += dburst - 1;
+ continue;
+ }
+
+write_protect:
+ ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
+ "Protect flash...\n");
+ rval = qla24xx_protect_flash(vha);
+ if (rval) {
+ qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_UNLOCK);
+ ql_log(ql_log_warn, vha, 0x7099,
+ "Failed protect flash\n");
+ }
+
+ if (reset_to_rom == true) {
+ /* Schedule DPC to restart the RISC */
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+
+ rval = qla2x00_wait_for_hba_online(vha);
+ if (rval != QLA_SUCCESS)
+ ql_log(ql_log_warn, vha, 0xffff,
+ "Adapter did not come out of reset\n");
+ }
+
+done:
+ if (optrom)
+ dma_free_coherent(&ha->pdev->dev,
+ OPTROM_BURST_SIZE, optrom, optrom_dma);
+
+ return rval;
+}
+
int
qla24xx_write_optrom_data(struct scsi_qla_host *vha, void *buf,
uint32_t offset, uint32_t length)
@@ -2635,8 +2961,12 @@ qla24xx_write_optrom_data(struct scsi_qla_host *vha, void *buf,
set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
/* Go with write. */
- rval = qla24xx_write_flash_data(vha, buf, offset >> 2,
- length >> 2);
+ if (IS_QLA28XX(ha))
+ rval = qla28xx_write_flash_data(vha, (uint32_t *)buf,
+ offset >> 2, length >> 2);
+ else
+ rval = qla24xx_write_flash_data(vha, (uint32_t *)buf,
+ offset >> 2, length >> 2);
clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
scsi_unblock_requests(vha->host);
@@ -3151,6 +3481,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
faddr = ha->flt_region_fw;
if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+ qla27xx_get_active_image(vha, &active_regions);
if (active_regions.global == QLA27XX_SECONDARY_IMAGE)
faddr = ha->flt_region_fw_sec;
}