mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
sfc: deploy devlink flash images to NIC over MCDI
Use MC_CMD_NVRAM_* wrappers to write the firmware to the partition identified from the image header. Signed-off-by: Edward Cree <ecree.xilinx@gmail.com> Link: https://patch.msgid.link/a746335876b621b3e54cf4e49948148e349a1745.1739186253.git.ecree.xilinx@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
d41987e906
commit
3ed63980ae
3 changed files with 231 additions and 3 deletions
|
@ -1003,6 +1003,7 @@ int efx_init_struct(struct efx_nic *efx, struct pci_dev *pci_dev)
|
||||||
INIT_LIST_HEAD(&efx->vf_reps);
|
INIT_LIST_HEAD(&efx->vf_reps);
|
||||||
INIT_WORK(&efx->mac_work, efx_mac_work);
|
INIT_WORK(&efx->mac_work, efx_mac_work);
|
||||||
init_waitqueue_head(&efx->flush_wq);
|
init_waitqueue_head(&efx->flush_wq);
|
||||||
|
mutex_init(&efx->reflash_mutex);
|
||||||
|
|
||||||
efx->tx_queues_per_channel = 1;
|
efx->tx_queues_per_channel = 1;
|
||||||
efx->rxq_entries = EFX_DEFAULT_DMAQ_SIZE;
|
efx->rxq_entries = EFX_DEFAULT_DMAQ_SIZE;
|
||||||
|
|
|
@ -255,13 +255,151 @@ static int efx_reflash_parse_firmware_data(const struct firmware *fw,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Limit the number of status updates during the erase or write phases */
|
||||||
|
#define EFX_DEVLINK_STATUS_UPDATE_COUNT 50
|
||||||
|
|
||||||
|
/* Expected timeout for the efx_mcdi_nvram_update_finish_polled() */
|
||||||
|
#define EFX_DEVLINK_UPDATE_FINISH_TIMEOUT 900
|
||||||
|
|
||||||
|
/* Ideal erase chunk size. This is a balance between minimising the number of
|
||||||
|
* MCDI requests to erase an entire partition whilst avoiding tripping the MCDI
|
||||||
|
* RPC timeout.
|
||||||
|
*/
|
||||||
|
#define EFX_NVRAM_ERASE_IDEAL_CHUNK_SIZE (64 * 1024)
|
||||||
|
|
||||||
|
static int efx_reflash_erase_partition(struct efx_nic *efx,
|
||||||
|
struct netlink_ext_ack *extack,
|
||||||
|
struct devlink *devlink, u32 type,
|
||||||
|
size_t partition_size,
|
||||||
|
size_t align)
|
||||||
|
{
|
||||||
|
size_t chunk, offset, next_update;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Partitions that cannot be erased or do not require erase before
|
||||||
|
* write are advertised with a erase alignment/sector size of zero.
|
||||||
|
*/
|
||||||
|
if (align == 0)
|
||||||
|
/* Nothing to do */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (partition_size % align)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Erase the entire NVRAM partition a chunk at a time to avoid
|
||||||
|
* potentially tripping the MCDI RPC timeout.
|
||||||
|
*/
|
||||||
|
if (align >= EFX_NVRAM_ERASE_IDEAL_CHUNK_SIZE)
|
||||||
|
chunk = align;
|
||||||
|
else
|
||||||
|
chunk = rounddown(EFX_NVRAM_ERASE_IDEAL_CHUNK_SIZE, align);
|
||||||
|
|
||||||
|
for (offset = 0, next_update = 0; offset < partition_size; offset += chunk) {
|
||||||
|
if (offset >= next_update) {
|
||||||
|
devlink_flash_update_status_notify(devlink, "Erasing",
|
||||||
|
NULL, offset,
|
||||||
|
partition_size);
|
||||||
|
next_update += partition_size / EFX_DEVLINK_STATUS_UPDATE_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk = min_t(size_t, partition_size - offset, chunk);
|
||||||
|
rc = efx_mcdi_nvram_erase(efx, type, offset, chunk);
|
||||||
|
if (rc) {
|
||||||
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||||
|
"Erase failed for NVRAM partition %#x at %#zx-%#zx",
|
||||||
|
type, offset, offset + chunk - 1);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
devlink_flash_update_status_notify(devlink, "Erasing", NULL,
|
||||||
|
partition_size, partition_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int efx_reflash_write_partition(struct efx_nic *efx,
|
||||||
|
struct netlink_ext_ack *extack,
|
||||||
|
struct devlink *devlink, u32 type,
|
||||||
|
const u8 *data, size_t data_size,
|
||||||
|
size_t align)
|
||||||
|
{
|
||||||
|
size_t write_max, chunk, offset, next_update;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (align == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Write the NVRAM partition in chunks that are the largest multiple
|
||||||
|
* of the partition's required write alignment that will fit into the
|
||||||
|
* MCDI NVRAM_WRITE RPC payload.
|
||||||
|
*/
|
||||||
|
if (efx->type->mcdi_max_ver < 2)
|
||||||
|
write_max = MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_LEN *
|
||||||
|
MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_MAXNUM;
|
||||||
|
else
|
||||||
|
write_max = MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_LEN *
|
||||||
|
MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_MAXNUM_MCDI2;
|
||||||
|
chunk = rounddown(write_max, align);
|
||||||
|
|
||||||
|
for (offset = 0, next_update = 0; offset + chunk <= data_size; offset += chunk) {
|
||||||
|
if (offset >= next_update) {
|
||||||
|
devlink_flash_update_status_notify(devlink, "Writing",
|
||||||
|
NULL, offset,
|
||||||
|
data_size);
|
||||||
|
next_update += data_size / EFX_DEVLINK_STATUS_UPDATE_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = efx_mcdi_nvram_write(efx, type, offset, data + offset, chunk);
|
||||||
|
if (rc) {
|
||||||
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||||
|
"Write failed for NVRAM partition %#x at %#zx-%#zx",
|
||||||
|
type, offset, offset + chunk - 1);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Round up left over data to satisfy write alignment */
|
||||||
|
if (offset < data_size) {
|
||||||
|
size_t remaining = data_size - offset;
|
||||||
|
u8 *buf;
|
||||||
|
|
||||||
|
if (offset >= next_update)
|
||||||
|
devlink_flash_update_status_notify(devlink, "Writing",
|
||||||
|
NULL, offset,
|
||||||
|
data_size);
|
||||||
|
|
||||||
|
chunk = roundup(remaining, align);
|
||||||
|
buf = kmalloc(chunk, GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
memcpy(buf, data + offset, remaining);
|
||||||
|
memset(buf + remaining, 0xFF, chunk - remaining);
|
||||||
|
rc = efx_mcdi_nvram_write(efx, type, offset, buf, chunk);
|
||||||
|
kfree(buf);
|
||||||
|
if (rc) {
|
||||||
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||||
|
"Write failed for NVRAM partition %#x at %#zx-%#zx",
|
||||||
|
type, offset, offset + chunk - 1);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
devlink_flash_update_status_notify(devlink, "Writing", NULL, data_size,
|
||||||
|
data_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int efx_reflash_flash_firmware(struct efx_nic *efx, const struct firmware *fw,
|
int efx_reflash_flash_firmware(struct efx_nic *efx, const struct firmware *fw,
|
||||||
struct netlink_ext_ack *extack)
|
struct netlink_ext_ack *extack)
|
||||||
{
|
{
|
||||||
|
size_t data_size, size, erase_align, write_align;
|
||||||
struct devlink *devlink = efx->devlink;
|
struct devlink *devlink = efx->devlink;
|
||||||
u32 type, data_subtype;
|
u32 type, data_subtype, subtype;
|
||||||
size_t data_size;
|
|
||||||
const u8 *data;
|
const u8 *data;
|
||||||
|
bool protected;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (!efx_has_cap(efx, BUNDLE_UPDATE)) {
|
if (!efx_has_cap(efx, BUNDLE_UPDATE)) {
|
||||||
|
@ -279,8 +417,95 @@ int efx_reflash_flash_firmware(struct efx_nic *efx, const struct firmware *fw,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = -EOPNOTSUPP;
|
mutex_lock(&efx->reflash_mutex);
|
||||||
|
|
||||||
|
rc = efx_mcdi_nvram_metadata(efx, type, &subtype, NULL, NULL, 0);
|
||||||
|
if (rc) {
|
||||||
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||||
|
"Metadata query for NVRAM partition %#x failed",
|
||||||
|
type);
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subtype != data_subtype) {
|
||||||
|
NL_SET_ERR_MSG_MOD(extack,
|
||||||
|
"Firmware image is not appropriate for this adapter");
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = efx_mcdi_nvram_info(efx, type, &size, &erase_align, &write_align,
|
||||||
|
&protected);
|
||||||
|
if (rc) {
|
||||||
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||||
|
"Info query for NVRAM partition %#x failed",
|
||||||
|
type);
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (protected) {
|
||||||
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||||
|
"NVRAM partition %#x is protected",
|
||||||
|
type);
|
||||||
|
rc = -EPERM;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write_align == 0) {
|
||||||
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||||
|
"NVRAM partition %#x is not writable",
|
||||||
|
type);
|
||||||
|
rc = -EACCES;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (erase_align != 0 && size % erase_align) {
|
||||||
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||||
|
"NVRAM partition %#x has a bad partition table entry, can't erase it",
|
||||||
|
type);
|
||||||
|
rc = -EACCES;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_size > size) {
|
||||||
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||||
|
"Firmware image is too big for NVRAM partition %#x",
|
||||||
|
type);
|
||||||
|
rc = -EFBIG;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
devlink_flash_update_status_notify(devlink, "Starting update", NULL, 0, 0);
|
||||||
|
|
||||||
|
rc = efx_mcdi_nvram_update_start(efx, type);
|
||||||
|
if (rc) {
|
||||||
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||||
|
"Update start request for NVRAM partition %#x failed",
|
||||||
|
type);
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = efx_reflash_erase_partition(efx, extack, devlink, type, size,
|
||||||
|
erase_align);
|
||||||
|
if (rc)
|
||||||
|
goto out_update_finish;
|
||||||
|
|
||||||
|
rc = efx_reflash_write_partition(efx, extack, devlink, type, data,
|
||||||
|
data_size, write_align);
|
||||||
|
if (rc)
|
||||||
|
goto out_update_finish;
|
||||||
|
|
||||||
|
devlink_flash_update_timeout_notify(devlink, "Finishing update", NULL,
|
||||||
|
EFX_DEVLINK_UPDATE_FINISH_TIMEOUT);
|
||||||
|
|
||||||
|
out_update_finish:
|
||||||
|
if (rc)
|
||||||
|
/* Don't obscure the return code from an earlier failure */
|
||||||
|
efx_mcdi_nvram_update_finish(efx, type, EFX_UPDATE_FINISH_ABORT);
|
||||||
|
else
|
||||||
|
rc = efx_mcdi_nvram_update_finish_polled(efx, type);
|
||||||
|
out_unlock:
|
||||||
|
mutex_unlock(&efx->reflash_mutex);
|
||||||
out:
|
out:
|
||||||
devlink_flash_update_status_notify(devlink, rc ? "Update failed" :
|
devlink_flash_update_status_notify(devlink, rc ? "Update failed" :
|
||||||
"Update complete",
|
"Update complete",
|
||||||
|
|
|
@ -1006,6 +1006,7 @@ struct efx_mae;
|
||||||
* @dl_port: devlink port associated with the PF
|
* @dl_port: devlink port associated with the PF
|
||||||
* @mem_bar: The BAR that is mapped into membase.
|
* @mem_bar: The BAR that is mapped into membase.
|
||||||
* @reg_base: Offset from the start of the bar to the function control window.
|
* @reg_base: Offset from the start of the bar to the function control window.
|
||||||
|
* @reflash_mutex: Mutex for serialising firmware reflash operations.
|
||||||
* @monitor_work: Hardware monitor workitem
|
* @monitor_work: Hardware monitor workitem
|
||||||
* @biu_lock: BIU (bus interface unit) lock
|
* @biu_lock: BIU (bus interface unit) lock
|
||||||
* @last_irq_cpu: Last CPU to handle a possible test interrupt. This
|
* @last_irq_cpu: Last CPU to handle a possible test interrupt. This
|
||||||
|
@ -1191,6 +1192,7 @@ struct efx_nic {
|
||||||
struct devlink_port *dl_port;
|
struct devlink_port *dl_port;
|
||||||
unsigned int mem_bar;
|
unsigned int mem_bar;
|
||||||
u32 reg_base;
|
u32 reg_base;
|
||||||
|
struct mutex reflash_mutex;
|
||||||
|
|
||||||
/* The following fields may be written more often */
|
/* The following fields may be written more often */
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue