Commit fd118a77 authored by Edward Cree's avatar Edward Cree Committed by Jakub Kicinski
Browse files

sfc: parse headers of devlink flash images



This parsing is necessary to obtain the metadata which will be
 used in a subsequent patch to write the image to the device.

Signed-off-by: default avatarEdward Cree <ecree.xilinx@gmail.com>
Link: https://patch.msgid.link/65318300f3f1b1462925f917f7c0d0ac833955ae.1739186253.git.ecree.xilinx@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent f4b87edb
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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 \
+13 −0
Original line number Diff line number Diff line
@@ -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,
};

+289 −0
Original line number Diff line number Diff line
// 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;
}

int efx_reflash_flash_firmware(struct efx_nic *efx, const struct firmware *fw,
			       struct netlink_ext_ack *extack)
{
	struct devlink *devlink = efx->devlink;
	u32 type, data_subtype;
	size_t data_size;
	const u8 *data;
	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;
	}

	rc = -EOPNOTSUPP;

out:
	devlink_flash_update_status_notify(devlink, rc ? "Update failed" :
							 "Update complete",
					   NULL, 0, 0);
	return rc;
}
+20 −0
Original line number Diff line number Diff line
/* 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 −0
Original line number Diff line number Diff line
/* 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 */