scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx

Add BSG vendor commands for loading and dumping MPI firmware on 29xx
adapters.  This extends the existing BSG infrastructure with the
necessary mailbox wrappers and flash helpers for MPI operations.

Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Link: https://patch.msgid.link/20260723050413.3897522-9-njavali@marvell.com
Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
This commit is contained in:
Manish Rangankar
2026-07-23 10:33:25 +05:30
committed by Martin K. Petersen (Oracle)
parent 2efa08b894
commit 514664aa30
6 changed files with 393 additions and 0 deletions

View File

@@ -1768,6 +1768,203 @@ static int qla29xx_bsg_flash_block_write(struct bsg_job *bsg_job)
return 0;
}
static int
qla29xx_mpi_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
uint32_t start, uint8_t is_update)
{
struct qla_hw_data *ha = vha->hw;
if (unlikely(pci_channel_offline(ha->pdev)))
return -EINVAL;
if (ha->optrom_state != QLA_SWAITING) {
ql_log(ql_log_info, vha, 0x7068,
"optrom_state %d.\n", ha->optrom_state);
return -EBUSY;
}
ha->optrom_region_start = start;
if (is_update) {
ha->optrom_region_size = bsg_job->request_payload.payload_len;
ha->optrom_state = QLA_SWRITING;
} else {
ha->optrom_region_size = bsg_job->reply_payload.payload_len;
ha->optrom_state = QLA_SREADING;
}
ha->optrom_buffer = vzalloc(ha->optrom_region_size);
if (!ha->optrom_buffer) {
ql_log(ql_log_warn, vha, 0x7069,
"%s: Unable to allocate memory for optrom retrieval (%x)\n",
__func__, ha->optrom_region_size);
ha->optrom_state = QLA_SWAITING;
return -ENOMEM;
}
return 0;
}
static int qla2900_bsg_dump_mpi(struct bsg_job *bsg_job)
{
struct fc_bsg_request *bsg_req = bsg_job->request;
struct fc_bsg_reply *bsg_reply = bsg_job->reply;
struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
struct qla_load_dump_mpi *dmcmd;
uint16_t opts = 0;
int rval = 0;
if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
2 * sizeof(uint32_t) + sizeof(struct qla_load_dump_mpi))
return -EINVAL;
if (!IS_QLA29XX(ha)) {
ql_log(ql_log_warn, vha, 0x706c,
"%s: MPI dump not supported on this adapter.\n",
__func__);
return -EINVAL;
}
dmcmd =
(struct qla_load_dump_mpi *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
ql_log(ql_log_info, vha, 0xffff,
"%s: mpi_address 0x%x mpi options 0x%x length 0x%x\n",
__func__, dmcmd->mpi_address, dmcmd->options, dmcmd->length);
mutex_lock(&ha->optrom_mutex);
rval = qla29xx_mpi_optrom_setup(bsg_job, vha, dmcmd->mpi_address, 1);
if (rval) {
mutex_unlock(&ha->optrom_mutex);
return rval;
}
sg_copy_to_buffer(bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, ha->optrom_buffer,
ha->optrom_region_size);
ql_dump_buffer(ql_dbg_init, vha, 0x00d7, ha->optrom_buffer,
ha->optrom_region_size);
check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_SECURE_ENABLE,
BIT_3);
check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_OTP_PROV, BIT_4);
check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_DEV_CSR, BIT_5);
check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_AUTH_CMD_BIN,
BIT_6);
check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_MLDSA_ALGO,
BIT_9);
rval = qla29xx_mpi_optrom_data(vha, opts, ha->optrom_buffer,
ha->optrom_region_start,
ha->optrom_region_size,
QLA29XX_MPI_OP_DUMP);
if (rval) {
ql_log(ql_log_warn, vha, 0xffff,
"%s failed mpi dump %x\n", __func__, rval);
bsg_reply->result = -EINVAL;
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
EXT_STATUS_MAILBOX;
} else {
bsg_reply->result = DID_OK;
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
EXT_STATUS_OK;
}
vfree(ha->optrom_buffer);
ha->optrom_buffer = NULL;
ha->optrom_state = QLA_SWAITING;
mutex_unlock(&ha->optrom_mutex);
bsg_job->reply_len = sizeof(struct fc_bsg_reply);
bsg_job_done(bsg_job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len);
return 0;
}
static int qla2900_bsg_load_mpi(struct bsg_job *bsg_job)
{
struct fc_bsg_request *bsg_req = bsg_job->request;
struct fc_bsg_reply *bsg_reply = bsg_job->reply;
struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
struct qla_load_dump_mpi *lmcmd;
uint16_t opts = 0;
int rval = 0;
if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
2 * sizeof(uint32_t) + sizeof(struct qla_load_dump_mpi))
return -EINVAL;
if (!IS_QLA29XX(ha)) {
ql_log(ql_log_warn, vha, 0x706d,
"%s: MPI load not supported on this adapter.\n",
__func__);
return -EINVAL;
}
lmcmd =
(struct qla_load_dump_mpi *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
ql_log(ql_log_info, vha, 0xffff,
"%s: mpi_address 0x%x mpi options 0x%x length 0x%x\n",
__func__, lmcmd->mpi_address, lmcmd->options, lmcmd->length);
mutex_lock(&ha->optrom_mutex);
rval = qla29xx_mpi_optrom_setup(bsg_job, vha, lmcmd->mpi_address, 0);
if (rval) {
mutex_unlock(&ha->optrom_mutex);
return rval;
}
check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_SECURE_ENABLE,
BIT_3);
check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_DEV_CSR, BIT_5);
check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_SHADOW_REGS,
BIT_7);
check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_CA_CSR, BIT_8);
check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_MLDSA_ALGO,
BIT_9);
check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_MLDSA_SIGNATURE,
BIT_10);
rval = qla29xx_mpi_optrom_data(vha, opts, ha->optrom_buffer,
ha->optrom_region_start,
ha->optrom_region_size,
QLA29XX_MPI_OP_LOAD);
if (rval) {
ql_log(ql_log_warn, vha, 0xffff,
"%s failed mpi load %x\n", __func__, rval);
bsg_reply->result = -EINVAL;
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
EXT_STATUS_MAILBOX;
} else {
bsg_reply->result = DID_OK;
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
EXT_STATUS_OK;
}
ql_dump_buffer(ql_dbg_init, vha, 0x00d7, ha->optrom_buffer,
ha->optrom_region_size);
sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt,
ha->optrom_buffer,
ha->optrom_region_size);
bsg_reply->reply_payload_rcv_len = ha->optrom_region_size;
vfree(ha->optrom_buffer);
ha->optrom_buffer = NULL;
ha->optrom_state = QLA_SWAITING;
mutex_unlock(&ha->optrom_mutex);
bsg_job->reply_len = sizeof(struct fc_bsg_reply);
bsg_job_done(bsg_job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len);
return rval;
}
static int
qla2x00_update_fru_versions(struct bsg_job *bsg_job)
{
@@ -3227,6 +3424,12 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j
case QL_VND_WRITE_FLASH_BLOCK:
return qla29xx_bsg_flash_block_write(bsg_job);
case QL_VND_LOAD_MPI:
return qla2900_bsg_load_mpi(bsg_job);
case QL_VND_DUMP_MPI:
return qla2900_bsg_dump_mpi(bsg_job);
default:
return -ENOSYS;
}

View File

@@ -42,6 +42,8 @@
#define QL_VND_IMG_SET_VALID 0x30
#define QL_VND_READ_FLASH_BLOCK 0x33
#define QL_VND_WRITE_FLASH_BLOCK 0x34
#define QL_VND_LOAD_MPI 0x35
#define QL_VND_DUMP_MPI 0x36
/* BSG Vendor specific subcode returns */
#define EXT_STATUS_OK 0
@@ -99,6 +101,22 @@ struct qla_block_rw {
uint8_t reserved[44];
} __packed;
struct qla_load_dump_mpi {
uint32_t mpi_address;
uint32_t length;
uint32_t options;
#define QLA_LDM_SECURE_ENABLE 0x1
#define QLA_LDM_OTP_PROV 0x2
#define QLA_LDM_DEV_CSR 0x4
#define QLA_LDM_AUTH_CMD_BIN 0x8
#define QLA_LDM_SHADOW_REGS 0x10
#define QLA_LDM_CA_CSR 0x20
#define QLA_LDM_MLDSA_ALGO 0x40
#define QLA_LDM_MLDSA_SIGNATURE 0x80
uint8_t reserved[52];
} __packed;
#define A84_ISSUE_WRITE_TYPE_CMD 0
#define A84_ISSUE_READ_TYPE_CMD 1
#define A84_CLEANUP_CMD 2

View File

@@ -1548,6 +1548,11 @@ typedef struct {
#define PD_STATE_PORT_LOGOUT 10
#define PD_STATE_WAIT_PORT_LOGOUT_ACK 11
enum qla29xx_mpi_optrom_op {
QLA29XX_MPI_OP_DUMP,
QLA29XX_MPI_OP_LOAD,
};
#define QLA_ZIO_MODE_6 (BIT_2 | BIT_1)
#define QLA_ZIO_DISABLED 0

View File

@@ -697,6 +697,11 @@ extern int qla29xx_write_optrom_data(struct scsi_qla_host *vha,
uint16_t reg_code, uint16_t opts,
void *buf, uint32_t offset,
uint32_t length);
extern int qla29xx_mpi_optrom_data(struct scsi_qla_host *vha, uint16_t opts,
void *buf, uint32_t offset, uint32_t length,
enum qla29xx_mpi_optrom_op op);
extern int qla29xx_load_dump_mpi(scsi_qla_host_t *vha, uint16_t opt,
uint32_t mpi_addr, uint32_t dlen, dma_addr_t req_dma);
/*
* Global Function Prototypes in qla_dbg.c source file.
*/

View File

@@ -7415,3 +7415,48 @@ int qla29xx_flash_block_write(scsi_qla_host_t *vha, dma_addr_t req_dma,
return rval;
}
int
qla29xx_load_dump_mpi(scsi_qla_host_t *vha, uint16_t opt, uint32_t mpi_addr,
uint32_t dlen, dma_addr_t req_dma)
{
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
int rval = 0;
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0xffff,
"Entered %s mpi_addr 0x%x len 0x%x opt 0x%x.\n",
__func__, mpi_addr, dlen, opt);
memset(mcp->mb, 0, sizeof(mcp->mb));
mcp->mb[0] = MBC_LOAD_DUMP_MPI_RAM;
mcp->mb[9] = opt;
mcp->mb[1] = LSW(mpi_addr);
mcp->mb[8] = MSW(mpi_addr);
mcp->mb[2] = MSW(req_dma);
mcp->mb[3] = LSW(req_dma);
mcp->mb[6] = MSW(MSD(req_dma));
mcp->mb[7] = LSW(MSD(req_dma));
mcp->mb[4] = MSW(dlen);
mcp->mb[5] = LSW(dlen);
mcp->out_mb = MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS)
ql_dbg(ql_dbg_mbx, vha, 0x110a,
"Failed=%x mb=(0x%x,0x%x).\n",
rval, mcp->mb[0], mcp->mb[1]);
else
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b,
"Done %s mb=(0x%x,0x%x,0x%x).\n", __func__,
mcp->mb[0], mcp->mb[1], mcp->mb[2]);
return rval;
}

View File

@@ -541,6 +541,123 @@ qla29xx_read_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
return NULL;
}
static void set_chunk_mpi_bits(uint16_t *options, int count, int total)
{
/* - Single chunk complete segment/image
* - 1st chunk of a segment/image
* - Last chunk of a segment/image
*/
if (total == 1)
*options |= BIT_2 | BIT_1;
else if (count == 0)
*options |= BIT_1;
else if (count == total - 1)
*options |= BIT_2;
}
/**
* qla29xx_mpi_optrom_data - Dump/load MPI optrom data for 29xx.
* @vha: Pointer to SCSI QLogic host structure.
* @opts: Options for the operation.
* @buf: Buffer to read from/write to.
* @offset: Offset into the device memory.
* @length: Length of data, in bytes.
* @op: Operation, either QLA29XX_MPI_OP_DUMP or QLA29XX_MPI_OP_LOAD.
*
* Returns:
* QLA_SUCCESS on success or an error code on failure.
*/
int
qla29xx_mpi_optrom_data(struct scsi_qla_host *vha, uint16_t opts, void *buf,
uint32_t offset, uint32_t length, enum qla29xx_mpi_optrom_op op)
{
struct qla_hw_data *ha = vha->hw;
dma_addr_t optrom_dma;
void *optrom;
uint32_t mpi_addr, mpi_size, burst = 0;
uint32_t total_chunks = 0;
uint32_t *dcode, *fwcode;
uint32_t chunk_count = 0;
int rval = -EINVAL;
optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
&optrom_dma, GFP_KERNEL);
if (!optrom) {
ql_log(ql_log_warn, vha, 0x0090,
"Unable to allocate memory for optrom burst read (%x KB).\n",
OPTROM_BURST_SIZE / 1024);
rval = -ENOMEM;
goto exit_mpi_op;
}
mpi_addr = offset;
dcode = (uint32_t *)optrom;
memset(dcode, 0, OPTROM_BURST_SIZE);
fwcode = (uint32_t *)buf;
mpi_size = length >> 2;
burst = OPTROM_BURST_SIZE >> 2;
total_chunks = (mpi_size + burst - 1) / burst;
while (mpi_size > 0) {
uint16_t options = 0;
if (burst > mpi_size)
burst = mpi_size;
set_chunk_mpi_bits(&options, chunk_count, total_chunks);
if (op == QLA29XX_MPI_OP_DUMP) {
options |= (BIT_0);
options |= opts;
ql_log(ql_log_info, vha, 0x008b,
"-> %s (DUMP): %#x <-(%#x words) (0x%x options) chunk (0x%x, 0x%x)\n",
__func__, mpi_addr, burst, options, chunk_count,
total_chunks);
memcpy(dcode, fwcode, burst * 4);
rval = qla29xx_load_dump_mpi(vha, options, mpi_addr, burst,
optrom_dma);
if (rval) {
ql_log(ql_log_fatal, vha, 0x0098,
"Failed dump mpi firmware.\n");
goto free_buf;
}
} else if (op == QLA29XX_MPI_OP_LOAD) {
options |= opts;
ql_log(ql_log_info, vha, 0x008b,
"-> %s (LOAD): %#x <-(%#x words) (0x%x options) chunk (0x%x, 0x%x)\n",
__func__, mpi_addr, burst, options, chunk_count,
total_chunks);
rval = qla29xx_load_dump_mpi(vha, options, mpi_addr, burst,
optrom_dma);
if (rval) {
ql_log(ql_log_fatal, vha, 0x0098,
"Failed load mpi firmware.\n");
goto free_buf;
}
memcpy(fwcode, dcode, burst * 4);
}
chunk_count++;
fwcode += burst;
mpi_addr += burst;
mpi_size -= burst;
}
rval = QLA_SUCCESS;
free_buf:
dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
optrom_dma);
exit_mpi_op:
return rval;
}
/*
* NVRAM support routines
*/