mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 08:33:17 +02:00
Merge branch 'sfc-support-devlink-flash'
Edward Cree says: ==================== sfc: support devlink flash Allow upgrading device firmware on Solarflare NICs through standard tools. ==================== Link: https://patch.msgid.link/cover.1739186252.git.ecree.xilinx@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
be1d2a1b15
|
|
@ -5,7 +5,7 @@ sfc devlink support
|
|||
===================
|
||||
|
||||
This document describes the devlink features implemented by the ``sfc``
|
||||
device driver for the ef100 device.
|
||||
device driver for the ef10 and ef100 devices.
|
||||
|
||||
Info versions
|
||||
=============
|
||||
|
|
@ -18,6 +18,10 @@ The ``sfc`` driver reports the following versions
|
|||
* - Name
|
||||
- Type
|
||||
- Description
|
||||
* - ``fw.bundle_id``
|
||||
- stored
|
||||
- Version of the firmware "bundle" image that was last used to update
|
||||
multiple components.
|
||||
* - ``fw.mgmt.suc``
|
||||
- running
|
||||
- For boards where the management function is split between multiple
|
||||
|
|
@ -55,3 +59,13 @@ The ``sfc`` driver reports the following versions
|
|||
* - ``fw.uefi``
|
||||
- running
|
||||
- UEFI driver version (No UNDI support).
|
||||
|
||||
Flash Update
|
||||
============
|
||||
|
||||
The ``sfc`` driver implements support for flash update using the
|
||||
``devlink-flash`` interface. It supports updating the device flash using a
|
||||
combined flash image ("bundle") that contains multiple components (on ef10,
|
||||
typically ``fw.mgmt``, ``fw.app``, ``fw.exprom`` and ``fw.uefi``).
|
||||
|
||||
The driver does not support any overwrite mask flags.
|
||||
|
|
|
|||
|
|
@ -38,8 +38,9 @@ config SFC_MTD
|
|||
default y
|
||||
help
|
||||
This exposes the on-board flash and/or EEPROM as MTD devices
|
||||
(e.g. /dev/mtd1). This is required to update the firmware or
|
||||
the boot configuration under Linux.
|
||||
(e.g. /dev/mtd1). This is required to update the boot
|
||||
configuration under Linux, or use some older userland tools to
|
||||
update the firmware.
|
||||
config SFC_MCDI_MON
|
||||
bool "Solarflare SFC9100-family hwmon support"
|
||||
depends on SFC && HWMON && !(SFC=y && HWMON=m)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \
|
|||
mcdi_functions.o mcdi_filters.o mcdi_mon.o \
|
||||
ef100.o ef100_nic.o ef100_netdev.o \
|
||||
ef100_ethtool.o ef100_rx.o ef100_tx.o \
|
||||
efx_devlink.o
|
||||
efx_devlink.o efx_reflash.o
|
||||
sfc-$(CONFIG_SFC_MTD) += mtd.o
|
||||
sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \
|
||||
mae.o tc.o tc_bindings.o tc_counters.o \
|
||||
|
|
|
|||
|
|
@ -3501,7 +3501,7 @@ static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
|
|||
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_METADATA_IN_LEN);
|
||||
MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_METADATA_OUT_LENMAX);
|
||||
const struct efx_ef10_nvram_type_info *info;
|
||||
size_t size, erase_size, outlen;
|
||||
size_t size, erase_size, write_size, outlen;
|
||||
int type_idx = 0;
|
||||
bool protected;
|
||||
int rc;
|
||||
|
|
@ -3516,7 +3516,8 @@ static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
|
|||
if (info->port != efx_port_num(efx))
|
||||
return -ENODEV;
|
||||
|
||||
rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &protected);
|
||||
rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &write_size,
|
||||
&protected);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (protected &&
|
||||
|
|
@ -3561,6 +3562,8 @@ static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
|
|||
if (!erase_size)
|
||||
part->common.mtd.flags |= MTD_NO_ERASE;
|
||||
|
||||
part->common.mtd.writesize = write_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1003,6 +1003,7 @@ int efx_init_struct(struct efx_nic *efx, struct pci_dev *pci_dev)
|
|||
INIT_LIST_HEAD(&efx->vf_reps);
|
||||
INIT_WORK(&efx->mac_work, efx_mac_work);
|
||||
init_waitqueue_head(&efx->flush_wq);
|
||||
mutex_init(&efx->reflash_mutex);
|
||||
|
||||
efx->tx_queues_per_channel = 1;
|
||||
efx->rxq_entries = EFX_DEFAULT_DMAQ_SIZE;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "mae.h"
|
||||
#include "ef100_rep.h"
|
||||
#endif
|
||||
#include "efx_reflash.h"
|
||||
|
||||
struct efx_devlink {
|
||||
struct efx_nic *efx;
|
||||
|
|
@ -615,7 +616,19 @@ static int efx_devlink_info_get(struct devlink *devlink,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int efx_devlink_flash_update(struct devlink *devlink,
|
||||
struct devlink_flash_update_params *params,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct efx_devlink *devlink_private = devlink_priv(devlink);
|
||||
struct efx_nic *efx = devlink_private->efx;
|
||||
|
||||
return efx_reflash_flash_firmware(efx, params->fw, extack);
|
||||
}
|
||||
|
||||
static const struct devlink_ops sfc_devlink_ops = {
|
||||
.supported_flash_update_params = 0,
|
||||
.flash_update = efx_devlink_flash_update,
|
||||
.info_get = efx_devlink_info_get,
|
||||
};
|
||||
|
||||
|
|
|
|||
514
drivers/net/ethernet/sfc/efx_reflash.c
Normal file
514
drivers/net/ethernet/sfc/efx_reflash.c
Normal file
|
|
@ -0,0 +1,514 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/****************************************************************************
|
||||
* Driver for AMD network controllers and boards
|
||||
* Copyright (C) 2025, Advanced Micro Devices, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/crc32.h>
|
||||
#include <net/devlink.h>
|
||||
#include "efx_reflash.h"
|
||||
#include "net_driver.h"
|
||||
#include "fw_formats.h"
|
||||
#include "mcdi_pcol.h"
|
||||
#include "mcdi.h"
|
||||
|
||||
/* Try to parse a Reflash header at the specified offset */
|
||||
static bool efx_reflash_parse_reflash_header(const struct firmware *fw,
|
||||
size_t header_offset, u32 *type,
|
||||
u32 *subtype, const u8 **data,
|
||||
size_t *data_size)
|
||||
{
|
||||
size_t header_end, trailer_offset, trailer_end;
|
||||
u32 magic, version, payload_size, header_len;
|
||||
const u8 *header, *trailer;
|
||||
u32 expected_crc, crc;
|
||||
|
||||
if (check_add_overflow(header_offset, EFX_REFLASH_HEADER_LENGTH_OFST +
|
||||
EFX_REFLASH_HEADER_LENGTH_LEN,
|
||||
&header_end))
|
||||
return false;
|
||||
if (fw->size < header_end)
|
||||
return false;
|
||||
|
||||
header = fw->data + header_offset;
|
||||
magic = get_unaligned_le32(header + EFX_REFLASH_HEADER_MAGIC_OFST);
|
||||
if (magic != EFX_REFLASH_HEADER_MAGIC_VALUE)
|
||||
return false;
|
||||
|
||||
version = get_unaligned_le32(header + EFX_REFLASH_HEADER_VERSION_OFST);
|
||||
if (version != EFX_REFLASH_HEADER_VERSION_VALUE)
|
||||
return false;
|
||||
|
||||
payload_size = get_unaligned_le32(header + EFX_REFLASH_HEADER_PAYLOAD_SIZE_OFST);
|
||||
header_len = get_unaligned_le32(header + EFX_REFLASH_HEADER_LENGTH_OFST);
|
||||
if (check_add_overflow(header_offset, header_len, &trailer_offset) ||
|
||||
check_add_overflow(trailer_offset, payload_size, &trailer_offset) ||
|
||||
check_add_overflow(trailer_offset, EFX_REFLASH_TRAILER_LEN,
|
||||
&trailer_end))
|
||||
return false;
|
||||
if (fw->size < trailer_end)
|
||||
return false;
|
||||
|
||||
trailer = fw->data + trailer_offset;
|
||||
expected_crc = get_unaligned_le32(trailer + EFX_REFLASH_TRAILER_CRC_OFST);
|
||||
/* Addition could overflow u32, but not size_t since we already
|
||||
* checked trailer_offset didn't overflow. So cast to size_t first.
|
||||
*/
|
||||
crc = crc32_le(0, header, (size_t)header_len + payload_size);
|
||||
if (crc != expected_crc)
|
||||
return false;
|
||||
|
||||
*type = get_unaligned_le32(header + EFX_REFLASH_HEADER_FIRMWARE_TYPE_OFST);
|
||||
*subtype = get_unaligned_le32(header + EFX_REFLASH_HEADER_FIRMWARE_SUBTYPE_OFST);
|
||||
if (*type == EFX_REFLASH_FIRMWARE_TYPE_BUNDLE) {
|
||||
/* All the bundle data is written verbatim to NVRAM */
|
||||
*data = fw->data;
|
||||
*data_size = fw->size;
|
||||
} else {
|
||||
/* Other payload types strip the reflash header and trailer
|
||||
* from the data written to NVRAM
|
||||
*/
|
||||
*data = header + header_len;
|
||||
*data_size = payload_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Map from FIRMWARE_TYPE to NVRAM_PARTITION_TYPE */
|
||||
static int efx_reflash_partition_type(u32 type, u32 subtype,
|
||||
u32 *partition_type,
|
||||
u32 *partition_subtype)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (type) {
|
||||
case EFX_REFLASH_FIRMWARE_TYPE_BOOTROM:
|
||||
*partition_type = NVRAM_PARTITION_TYPE_EXPANSION_ROM;
|
||||
*partition_subtype = subtype;
|
||||
break;
|
||||
case EFX_REFLASH_FIRMWARE_TYPE_BUNDLE:
|
||||
*partition_type = NVRAM_PARTITION_TYPE_BUNDLE;
|
||||
*partition_subtype = subtype;
|
||||
break;
|
||||
default:
|
||||
/* Not supported */
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Try to parse a SmartNIC image header at the specified offset */
|
||||
static bool efx_reflash_parse_snic_header(const struct firmware *fw,
|
||||
size_t header_offset,
|
||||
u32 *partition_type,
|
||||
u32 *partition_subtype,
|
||||
const u8 **data, size_t *data_size)
|
||||
{
|
||||
u32 magic, version, payload_size, header_len, expected_crc, crc;
|
||||
size_t header_end, payload_end;
|
||||
const u8 *header;
|
||||
|
||||
if (check_add_overflow(header_offset, EFX_SNICIMAGE_HEADER_MINLEN,
|
||||
&header_end) ||
|
||||
fw->size < header_end)
|
||||
return false;
|
||||
|
||||
header = fw->data + header_offset;
|
||||
magic = get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_MAGIC_OFST);
|
||||
if (magic != EFX_SNICIMAGE_HEADER_MAGIC_VALUE)
|
||||
return false;
|
||||
|
||||
version = get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_VERSION_OFST);
|
||||
if (version != EFX_SNICIMAGE_HEADER_VERSION_VALUE)
|
||||
return false;
|
||||
|
||||
header_len = get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_LENGTH_OFST);
|
||||
if (check_add_overflow(header_offset, header_len, &header_end))
|
||||
return false;
|
||||
payload_size = get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_PAYLOAD_SIZE_OFST);
|
||||
if (check_add_overflow(header_end, payload_size, &payload_end) ||
|
||||
fw->size < payload_end)
|
||||
return false;
|
||||
|
||||
expected_crc = get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_CRC_OFST);
|
||||
|
||||
/* Calculate CRC omitting the expected CRC field itself */
|
||||
crc = crc32_le(~0, header, EFX_SNICIMAGE_HEADER_CRC_OFST);
|
||||
crc = ~crc32_le(crc,
|
||||
header + EFX_SNICIMAGE_HEADER_CRC_OFST +
|
||||
EFX_SNICIMAGE_HEADER_CRC_LEN,
|
||||
header_len + payload_size - EFX_SNICIMAGE_HEADER_CRC_OFST -
|
||||
EFX_SNICIMAGE_HEADER_CRC_LEN);
|
||||
if (crc != expected_crc)
|
||||
return false;
|
||||
|
||||
*partition_type =
|
||||
get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_PARTITION_TYPE_OFST);
|
||||
*partition_subtype =
|
||||
get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_PARTITION_SUBTYPE_OFST);
|
||||
*data = fw->data;
|
||||
*data_size = fw->size;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Try to parse a SmartNIC bundle header at the specified offset */
|
||||
static bool efx_reflash_parse_snic_bundle_header(const struct firmware *fw,
|
||||
size_t header_offset,
|
||||
u32 *partition_type,
|
||||
u32 *partition_subtype,
|
||||
const u8 **data,
|
||||
size_t *data_size)
|
||||
{
|
||||
u32 magic, version, bundle_type, header_len, expected_crc, crc;
|
||||
size_t header_end;
|
||||
const u8 *header;
|
||||
|
||||
if (check_add_overflow(header_offset, EFX_SNICBUNDLE_HEADER_LEN,
|
||||
&header_end))
|
||||
return false;
|
||||
if (fw->size < header_end)
|
||||
return false;
|
||||
|
||||
header = fw->data + header_offset;
|
||||
magic = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_MAGIC_OFST);
|
||||
if (magic != EFX_SNICBUNDLE_HEADER_MAGIC_VALUE)
|
||||
return false;
|
||||
|
||||
version = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_VERSION_OFST);
|
||||
if (version != EFX_SNICBUNDLE_HEADER_VERSION_VALUE)
|
||||
return false;
|
||||
|
||||
bundle_type = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_BUNDLE_TYPE_OFST);
|
||||
if (bundle_type != NVRAM_PARTITION_TYPE_BUNDLE)
|
||||
return false;
|
||||
|
||||
header_len = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_LENGTH_OFST);
|
||||
if (header_len != EFX_SNICBUNDLE_HEADER_LEN)
|
||||
return false;
|
||||
|
||||
expected_crc = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_CRC_OFST);
|
||||
crc = ~crc32_le(~0, header, EFX_SNICBUNDLE_HEADER_CRC_OFST);
|
||||
if (crc != expected_crc)
|
||||
return false;
|
||||
|
||||
*partition_type = NVRAM_PARTITION_TYPE_BUNDLE;
|
||||
*partition_subtype = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_BUNDLE_SUBTYPE_OFST);
|
||||
*data = fw->data;
|
||||
*data_size = fw->size;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Try to find a valid firmware payload in the firmware data.
|
||||
* When we recognise a valid header, we parse it for the partition type
|
||||
* (so we know where to ask the MC to write it to) and the location of
|
||||
* the data blob to write.
|
||||
*/
|
||||
static int efx_reflash_parse_firmware_data(const struct firmware *fw,
|
||||
u32 *partition_type,
|
||||
u32 *partition_subtype,
|
||||
const u8 **data, size_t *data_size)
|
||||
{
|
||||
size_t header_offset;
|
||||
u32 type, subtype;
|
||||
|
||||
/* Some packaging formats (such as CMS/PKCS#7 signed images)
|
||||
* prepend a header for which finding the size is a non-trivial
|
||||
* task, so step through the firmware data until we find a valid
|
||||
* header.
|
||||
*
|
||||
* The checks are intended to reject firmware data that is clearly not
|
||||
* in the expected format. They do not need to be exhaustive as the
|
||||
* running firmware will perform its own comprehensive validity and
|
||||
* compatibility checks during the update procedure.
|
||||
*
|
||||
* Firmware packages may contain multiple reflash images, e.g. a
|
||||
* bundle containing one or more other images. Only check the
|
||||
* outermost container by stopping after the first candidate image
|
||||
* found even it is for an unsupported partition type.
|
||||
*/
|
||||
for (header_offset = 0; header_offset < fw->size; header_offset++) {
|
||||
if (efx_reflash_parse_snic_bundle_header(fw, header_offset,
|
||||
partition_type,
|
||||
partition_subtype,
|
||||
data, data_size))
|
||||
return 0;
|
||||
|
||||
if (efx_reflash_parse_snic_header(fw, header_offset,
|
||||
partition_type,
|
||||
partition_subtype, data,
|
||||
data_size))
|
||||
return 0;
|
||||
|
||||
if (efx_reflash_parse_reflash_header(fw, header_offset, &type,
|
||||
&subtype, data, data_size))
|
||||
return efx_reflash_partition_type(type, subtype,
|
||||
partition_type,
|
||||
partition_subtype);
|
||||
}
|
||||
|
||||
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,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
size_t data_size, size, erase_align, write_align;
|
||||
struct devlink *devlink = efx->devlink;
|
||||
u32 type, data_subtype, subtype;
|
||||
const u8 *data;
|
||||
bool protected;
|
||||
int rc;
|
||||
|
||||
if (!efx_has_cap(efx, BUNDLE_UPDATE)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "NVRAM bundle updates are not supported by the firmware");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
devlink_flash_update_status_notify(devlink, "Checking update", NULL, 0, 0);
|
||||
|
||||
rc = efx_reflash_parse_firmware_data(fw, &type, &data_subtype, &data,
|
||||
&data_size);
|
||||
if (rc) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Firmware image validation check failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
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:
|
||||
devlink_flash_update_status_notify(devlink, rc ? "Update failed" :
|
||||
"Update complete",
|
||||
NULL, 0, 0);
|
||||
return rc;
|
||||
}
|
||||
20
drivers/net/ethernet/sfc/efx_reflash.h
Normal file
20
drivers/net/ethernet/sfc/efx_reflash.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/****************************************************************************
|
||||
* Driver for AMD network controllers and boards
|
||||
* Copyright (C) 2025, Advanced Micro Devices, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef _EFX_REFLASH_H
|
||||
#define _EFX_REFLASH_H
|
||||
|
||||
#include "net_driver.h"
|
||||
#include <linux/firmware.h>
|
||||
|
||||
int efx_reflash_flash_firmware(struct efx_nic *efx, const struct firmware *fw,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
||||
#endif /* _EFX_REFLASH_H */
|
||||
114
drivers/net/ethernet/sfc/fw_formats.h
Normal file
114
drivers/net/ethernet/sfc/fw_formats.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/****************************************************************************
|
||||
* Driver for AMD network controllers and boards
|
||||
* Copyright (C) 2025, Advanced Micro Devices, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef _EFX_FW_FORMATS_H
|
||||
#define _EFX_FW_FORMATS_H
|
||||
|
||||
/* Header layouts of firmware update images recognised by Efx NICs.
|
||||
* The sources-of-truth for these layouts are AMD internal documents
|
||||
* and sfregistry headers, neither of which are available externally
|
||||
* nor usable directly by the driver.
|
||||
*
|
||||
* While each format includes a 'magic number', these are at different
|
||||
* offsets in the various formats, and a legal header for one format
|
||||
* could have the right value in whichever field occupies that offset
|
||||
* to match another format's magic.
|
||||
* Besides, some packaging formats (such as CMS/PKCS#7 signed images)
|
||||
* prepend a header for which finding the size is a non-trivial task;
|
||||
* rather than trying to parse those headers, we search byte-by-byte
|
||||
* through the provided firmware image looking for a valid header.
|
||||
* Thus, format recognition has to include validation of the checksum
|
||||
* field, even though the firmware will validate that itself before
|
||||
* applying the image.
|
||||
*/
|
||||
|
||||
/* EF10 (Medford2, X2) "reflash" header format. Defined in SF-121352-AN */
|
||||
#define EFX_REFLASH_HEADER_MAGIC_OFST 0
|
||||
#define EFX_REFLASH_HEADER_MAGIC_LEN 4
|
||||
#define EFX_REFLASH_HEADER_MAGIC_VALUE 0x106F1A5
|
||||
|
||||
#define EFX_REFLASH_HEADER_VERSION_OFST 4
|
||||
#define EFX_REFLASH_HEADER_VERSION_LEN 4
|
||||
#define EFX_REFLASH_HEADER_VERSION_VALUE 4
|
||||
|
||||
#define EFX_REFLASH_HEADER_FIRMWARE_TYPE_OFST 8
|
||||
#define EFX_REFLASH_HEADER_FIRMWARE_TYPE_LEN 4
|
||||
#define EFX_REFLASH_FIRMWARE_TYPE_BOOTROM 0x2
|
||||
#define EFX_REFLASH_FIRMWARE_TYPE_BUNDLE 0xd
|
||||
|
||||
#define EFX_REFLASH_HEADER_FIRMWARE_SUBTYPE_OFST 12
|
||||
#define EFX_REFLASH_HEADER_FIRMWARE_SUBTYPE_LEN 4
|
||||
|
||||
#define EFX_REFLASH_HEADER_PAYLOAD_SIZE_OFST 16
|
||||
#define EFX_REFLASH_HEADER_PAYLOAD_SIZE_LEN 4
|
||||
|
||||
#define EFX_REFLASH_HEADER_LENGTH_OFST 20
|
||||
#define EFX_REFLASH_HEADER_LENGTH_LEN 4
|
||||
|
||||
/* Reflash trailer */
|
||||
#define EFX_REFLASH_TRAILER_CRC_OFST 0
|
||||
#define EFX_REFLASH_TRAILER_CRC_LEN 4
|
||||
|
||||
#define EFX_REFLASH_TRAILER_LEN \
|
||||
(EFX_REFLASH_TRAILER_CRC_OFST + EFX_REFLASH_TRAILER_CRC_LEN)
|
||||
|
||||
/* EF100 "SmartNIC image" header format.
|
||||
* Defined in sfregistry "src/layout/snic_image_hdr.h".
|
||||
*/
|
||||
#define EFX_SNICIMAGE_HEADER_MAGIC_OFST 16
|
||||
#define EFX_SNICIMAGE_HEADER_MAGIC_LEN 4
|
||||
#define EFX_SNICIMAGE_HEADER_MAGIC_VALUE 0x541C057A
|
||||
|
||||
#define EFX_SNICIMAGE_HEADER_VERSION_OFST 20
|
||||
#define EFX_SNICIMAGE_HEADER_VERSION_LEN 4
|
||||
#define EFX_SNICIMAGE_HEADER_VERSION_VALUE 1
|
||||
|
||||
#define EFX_SNICIMAGE_HEADER_LENGTH_OFST 24
|
||||
#define EFX_SNICIMAGE_HEADER_LENGTH_LEN 4
|
||||
|
||||
#define EFX_SNICIMAGE_HEADER_PARTITION_TYPE_OFST 36
|
||||
#define EFX_SNICIMAGE_HEADER_PARTITION_TYPE_LEN 4
|
||||
|
||||
#define EFX_SNICIMAGE_HEADER_PARTITION_SUBTYPE_OFST 40
|
||||
#define EFX_SNICIMAGE_HEADER_PARTITION_SUBTYPE_LEN 4
|
||||
|
||||
#define EFX_SNICIMAGE_HEADER_PAYLOAD_SIZE_OFST 60
|
||||
#define EFX_SNICIMAGE_HEADER_PAYLOAD_SIZE_LEN 4
|
||||
|
||||
#define EFX_SNICIMAGE_HEADER_CRC_OFST 64
|
||||
#define EFX_SNICIMAGE_HEADER_CRC_LEN 4
|
||||
|
||||
#define EFX_SNICIMAGE_HEADER_MINLEN 256
|
||||
|
||||
/* EF100 "SmartNIC bundle" header format. Defined in SF-122606-TC */
|
||||
#define EFX_SNICBUNDLE_HEADER_MAGIC_OFST 0
|
||||
#define EFX_SNICBUNDLE_HEADER_MAGIC_LEN 4
|
||||
#define EFX_SNICBUNDLE_HEADER_MAGIC_VALUE 0xB1001001
|
||||
|
||||
#define EFX_SNICBUNDLE_HEADER_VERSION_OFST 4
|
||||
#define EFX_SNICBUNDLE_HEADER_VERSION_LEN 4
|
||||
#define EFX_SNICBUNDLE_HEADER_VERSION_VALUE 1
|
||||
|
||||
#define EFX_SNICBUNDLE_HEADER_BUNDLE_TYPE_OFST 8
|
||||
#define EFX_SNICBUNDLE_HEADER_BUNDLE_TYPE_LEN 4
|
||||
|
||||
#define EFX_SNICBUNDLE_HEADER_BUNDLE_SUBTYPE_OFST 12
|
||||
#define EFX_SNICBUNDLE_HEADER_BUNDLE_SUBTYPE_LEN 4
|
||||
|
||||
#define EFX_SNICBUNDLE_HEADER_LENGTH_OFST 20
|
||||
#define EFX_SNICBUNDLE_HEADER_LENGTH_LEN 4
|
||||
|
||||
#define EFX_SNICBUNDLE_HEADER_CRC_OFST 224
|
||||
#define EFX_SNICBUNDLE_HEADER_CRC_LEN 4
|
||||
|
||||
#define EFX_SNICBUNDLE_HEADER_LEN \
|
||||
(EFX_SNICBUNDLE_HEADER_CRC_OFST + EFX_SNICBUNDLE_HEADER_CRC_LEN)
|
||||
|
||||
#endif /* _EFX_FW_FORMATS_H */
|
||||
|
|
@ -1625,12 +1625,15 @@ static int efx_new_mcdi_nvram_types(struct efx_nic *efx, u32 *number,
|
|||
return rc;
|
||||
}
|
||||
|
||||
#define EFX_MCDI_NVRAM_DEFAULT_WRITE_LEN 128
|
||||
|
||||
int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
|
||||
size_t *size_out, size_t *erase_size_out,
|
||||
bool *protected_out)
|
||||
size_t *write_size_out, bool *protected_out)
|
||||
{
|
||||
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_INFO_IN_LEN);
|
||||
MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_INFO_OUT_LEN);
|
||||
MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_INFO_V2_OUT_LEN);
|
||||
size_t write_size = 0;
|
||||
size_t outlen;
|
||||
int rc;
|
||||
|
||||
|
|
@ -1645,6 +1648,12 @@ int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (outlen >= MC_CMD_NVRAM_INFO_V2_OUT_LEN)
|
||||
write_size = MCDI_DWORD(outbuf, NVRAM_INFO_V2_OUT_WRITESIZE);
|
||||
else
|
||||
write_size = EFX_MCDI_NVRAM_DEFAULT_WRITE_LEN;
|
||||
|
||||
*write_size_out = write_size;
|
||||
*size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_SIZE);
|
||||
*erase_size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_ERASESIZE);
|
||||
*protected_out = !!(MCDI_DWORD(outbuf, NVRAM_INFO_OUT_FLAGS) &
|
||||
|
|
@ -2163,11 +2172,9 @@ int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type,
|
|||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SFC_MTD
|
||||
|
||||
#define EFX_MCDI_NVRAM_LEN_MAX 128
|
||||
|
||||
static int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
|
||||
int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
|
||||
{
|
||||
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_START_V2_IN_LEN);
|
||||
int rc;
|
||||
|
|
@ -2185,6 +2192,8 @@ static int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
|
|||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SFC_MTD
|
||||
|
||||
static int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
|
||||
loff_t offset, u8 *buffer, size_t length)
|
||||
{
|
||||
|
|
@ -2209,13 +2218,20 @@ static int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
|
||||
loff_t offset, const u8 *buffer, size_t length)
|
||||
#endif /* CONFIG_SFC_MTD */
|
||||
|
||||
int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
|
||||
loff_t offset, const u8 *buffer, size_t length)
|
||||
{
|
||||
MCDI_DECLARE_BUF(inbuf,
|
||||
MC_CMD_NVRAM_WRITE_IN_LEN(EFX_MCDI_NVRAM_LEN_MAX));
|
||||
efx_dword_t *inbuf;
|
||||
size_t inlen;
|
||||
int rc;
|
||||
|
||||
inlen = ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4);
|
||||
inbuf = kzalloc(inlen, GFP_KERNEL);
|
||||
if (!inbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type);
|
||||
MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset);
|
||||
MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length);
|
||||
|
|
@ -2223,14 +2239,14 @@ static int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
|
|||
|
||||
BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0);
|
||||
|
||||
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf,
|
||||
ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4),
|
||||
NULL, 0, NULL);
|
||||
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf, inlen, NULL, 0, NULL);
|
||||
kfree(inbuf);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
|
||||
loff_t offset, size_t length)
|
||||
int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type, loff_t offset,
|
||||
size_t length)
|
||||
{
|
||||
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_ERASE_IN_LEN);
|
||||
int rc;
|
||||
|
|
@ -2246,7 +2262,8 @@ static int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
|
||||
int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type,
|
||||
enum efx_update_finish_mode mode)
|
||||
{
|
||||
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_LEN);
|
||||
MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN);
|
||||
|
|
@ -2254,22 +2271,41 @@ static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
|
|||
int rc, rc2;
|
||||
|
||||
MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type);
|
||||
/* Always set this flag. Old firmware ignores it */
|
||||
MCDI_POPULATE_DWORD_1(inbuf, NVRAM_UPDATE_FINISH_V2_IN_FLAGS,
|
||||
|
||||
/* Old firmware doesn't support background update finish and abort
|
||||
* operations. Fallback to waiting if the requested mode is not
|
||||
* supported.
|
||||
*/
|
||||
if (!efx_has_cap(efx, NVRAM_UPDATE_POLL_VERIFY_RESULT) ||
|
||||
(!efx_has_cap(efx, NVRAM_UPDATE_ABORT_SUPPORTED) &&
|
||||
mode == EFX_UPDATE_FINISH_ABORT))
|
||||
mode = EFX_UPDATE_FINISH_WAIT;
|
||||
|
||||
MCDI_POPULATE_DWORD_4(inbuf, NVRAM_UPDATE_FINISH_V2_IN_FLAGS,
|
||||
NVRAM_UPDATE_FINISH_V2_IN_FLAG_REPORT_VERIFY_RESULT,
|
||||
1);
|
||||
(mode != EFX_UPDATE_FINISH_ABORT),
|
||||
NVRAM_UPDATE_FINISH_V2_IN_FLAG_RUN_IN_BACKGROUND,
|
||||
(mode == EFX_UPDATE_FINISH_BACKGROUND),
|
||||
NVRAM_UPDATE_FINISH_V2_IN_FLAG_POLL_VERIFY_RESULT,
|
||||
(mode == EFX_UPDATE_FINISH_POLL),
|
||||
NVRAM_UPDATE_FINISH_V2_IN_FLAG_ABORT,
|
||||
(mode == EFX_UPDATE_FINISH_ABORT));
|
||||
|
||||
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf),
|
||||
outbuf, sizeof(outbuf), &outlen);
|
||||
if (!rc && outlen >= MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN) {
|
||||
rc2 = MCDI_DWORD(outbuf, NVRAM_UPDATE_FINISH_V2_OUT_RESULT_CODE);
|
||||
if (rc2 != MC_CMD_NVRAM_VERIFY_RC_SUCCESS)
|
||||
if (rc2 != MC_CMD_NVRAM_VERIFY_RC_SUCCESS &&
|
||||
rc2 != MC_CMD_NVRAM_VERIFY_RC_PENDING)
|
||||
netif_err(efx, drv, efx->net_dev,
|
||||
"NVRAM update failed verification with code 0x%x\n",
|
||||
rc2);
|
||||
switch (rc2) {
|
||||
case MC_CMD_NVRAM_VERIFY_RC_SUCCESS:
|
||||
break;
|
||||
case MC_CMD_NVRAM_VERIFY_RC_PENDING:
|
||||
rc = -EAGAIN;
|
||||
break;
|
||||
case MC_CMD_NVRAM_VERIFY_RC_CMS_CHECK_FAILED:
|
||||
case MC_CMD_NVRAM_VERIFY_RC_MESSAGE_DIGEST_CHECK_FAILED:
|
||||
case MC_CMD_NVRAM_VERIFY_RC_SIGNATURE_CHECK_FAILED:
|
||||
|
|
@ -2284,6 +2320,8 @@ static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
|
|||
case MC_CMD_NVRAM_VERIFY_RC_NO_VALID_SIGNATURES:
|
||||
case MC_CMD_NVRAM_VERIFY_RC_NO_TRUSTED_APPROVERS:
|
||||
case MC_CMD_NVRAM_VERIFY_RC_NO_SIGNATURE_MATCH:
|
||||
case MC_CMD_NVRAM_VERIFY_RC_REJECT_TEST_SIGNED:
|
||||
case MC_CMD_NVRAM_VERIFY_RC_SECURITY_LEVEL_DOWNGRADE:
|
||||
rc = -EPERM;
|
||||
break;
|
||||
default:
|
||||
|
|
@ -2296,6 +2334,42 @@ static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
|
|||
return rc;
|
||||
}
|
||||
|
||||
#define EFX_MCDI_NVRAM_UPDATE_FINISH_INITIAL_POLL_DELAY_MS 5
|
||||
#define EFX_MCDI_NVRAM_UPDATE_FINISH_MAX_POLL_DELAY_MS 5000
|
||||
#define EFX_MCDI_NVRAM_UPDATE_FINISH_RETRIES 185
|
||||
|
||||
int efx_mcdi_nvram_update_finish_polled(struct efx_nic *efx, unsigned int type)
|
||||
{
|
||||
unsigned int delay = EFX_MCDI_NVRAM_UPDATE_FINISH_INITIAL_POLL_DELAY_MS;
|
||||
unsigned int retry = 0;
|
||||
int rc;
|
||||
|
||||
/* NVRAM updates can take a long time (e.g. up to 1 minute for bundle
|
||||
* images). Polling for NVRAM update completion ensures that other MCDI
|
||||
* commands can be issued before the background NVRAM update completes.
|
||||
*
|
||||
* The initial call either completes the update synchronously, or
|
||||
* returns -EAGAIN to indicate processing is continuing. In the latter
|
||||
* case, we poll for at least 900 seconds, at increasing intervals
|
||||
* (5ms, 50ms, 500ms, 5s).
|
||||
*/
|
||||
rc = efx_mcdi_nvram_update_finish(efx, type, EFX_UPDATE_FINISH_BACKGROUND);
|
||||
while (rc == -EAGAIN) {
|
||||
if (retry > EFX_MCDI_NVRAM_UPDATE_FINISH_RETRIES)
|
||||
return -ETIMEDOUT;
|
||||
retry++;
|
||||
|
||||
msleep(delay);
|
||||
if (delay < EFX_MCDI_NVRAM_UPDATE_FINISH_MAX_POLL_DELAY_MS)
|
||||
delay *= 10;
|
||||
|
||||
rc = efx_mcdi_nvram_update_finish(efx, type, EFX_UPDATE_FINISH_POLL);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SFC_MTD
|
||||
|
||||
int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start,
|
||||
size_t len, size_t *retlen, u8 *buffer)
|
||||
{
|
||||
|
|
@ -2389,7 +2463,8 @@ int efx_mcdi_mtd_sync(struct mtd_info *mtd)
|
|||
|
||||
if (part->updating) {
|
||||
part->updating = false;
|
||||
rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type);
|
||||
rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type,
|
||||
EFX_UPDATE_FINISH_WAIT);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, u32 dest_evq);
|
|||
int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out);
|
||||
int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
|
||||
size_t *size_out, size_t *erase_size_out,
|
||||
bool *protected_out);
|
||||
size_t *write_size_out, bool *protected_out);
|
||||
int efx_new_mcdi_nvram_test_all(struct efx_nic *efx);
|
||||
int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type,
|
||||
u32 *subtype, u16 version[4], char *desc,
|
||||
|
|
@ -424,6 +424,26 @@ static inline int efx_mcdi_mon_probe(struct efx_nic *efx) { return 0; }
|
|||
static inline void efx_mcdi_mon_remove(struct efx_nic *efx) {}
|
||||
#endif
|
||||
|
||||
int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type);
|
||||
int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
|
||||
loff_t offset, const u8 *buffer, size_t length);
|
||||
int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
|
||||
loff_t offset, size_t length);
|
||||
int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type,
|
||||
u32 *subtype, u16 version[4], char *desc,
|
||||
size_t descsize);
|
||||
|
||||
enum efx_update_finish_mode {
|
||||
EFX_UPDATE_FINISH_WAIT,
|
||||
EFX_UPDATE_FINISH_BACKGROUND,
|
||||
EFX_UPDATE_FINISH_POLL,
|
||||
EFX_UPDATE_FINISH_ABORT,
|
||||
};
|
||||
|
||||
int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type,
|
||||
enum efx_update_finish_mode mode);
|
||||
int efx_mcdi_nvram_update_finish_polled(struct efx_nic *efx, unsigned int type);
|
||||
|
||||
#ifdef CONFIG_SFC_MTD
|
||||
int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start, size_t len,
|
||||
size_t *retlen, u8 *buffer);
|
||||
|
|
|
|||
|
|
@ -1006,6 +1006,7 @@ struct efx_mae;
|
|||
* @dl_port: devlink port associated with the PF
|
||||
* @mem_bar: The BAR that is mapped into membase.
|
||||
* @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
|
||||
* @biu_lock: BIU (bus interface unit) lock
|
||||
* @last_irq_cpu: Last CPU to handle a possible test interrupt. This
|
||||
|
|
@ -1191,6 +1192,7 @@ struct efx_nic {
|
|||
struct devlink_port *dl_port;
|
||||
unsigned int mem_bar;
|
||||
u32 reg_base;
|
||||
struct mutex reflash_mutex;
|
||||
|
||||
/* The following fields may be written more often */
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user