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:
Jakub Kicinski 2025-02-11 17:45:45 -08:00
commit be1d2a1b15
12 changed files with 804 additions and 27 deletions

View File

@ -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.

View File

@ -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)

View File

@ -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 \

View File

@ -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;
}

View File

@ -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;

View File

@ -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,
};

View 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;
}

View 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 */

View 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 */

View File

@ -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;

View File

@ -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);

View File

@ -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 */