Skip to content

Commit fd118a7

Browse files
Edward Creekuba-moo
authored andcommitted
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: Edward Cree <ecree.xilinx@gmail.com> Link: https://patch.msgid.link/65318300f3f1b1462925f917f7c0d0ac833955ae.1739186253.git.ecree.xilinx@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent f4b87ed commit fd118a7

File tree

5 files changed

+437
-1
lines changed

5 files changed

+437
-1
lines changed

drivers/net/ethernet/sfc/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \
77
mcdi_functions.o mcdi_filters.o mcdi_mon.o \
88
ef100.o ef100_nic.o ef100_netdev.o \
99
ef100_ethtool.o ef100_rx.o ef100_tx.o \
10-
efx_devlink.o
10+
efx_devlink.o efx_reflash.o
1111
sfc-$(CONFIG_SFC_MTD) += mtd.o
1212
sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \
1313
mae.o tc.o tc_bindings.o tc_counters.o \

drivers/net/ethernet/sfc/efx_devlink.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "mae.h"
2020
#include "ef100_rep.h"
2121
#endif
22+
#include "efx_reflash.h"
2223

2324
struct efx_devlink {
2425
struct efx_nic *efx;
@@ -615,7 +616,19 @@ static int efx_devlink_info_get(struct devlink *devlink,
615616
return 0;
616617
}
617618

619+
static int efx_devlink_flash_update(struct devlink *devlink,
620+
struct devlink_flash_update_params *params,
621+
struct netlink_ext_ack *extack)
622+
{
623+
struct efx_devlink *devlink_private = devlink_priv(devlink);
624+
struct efx_nic *efx = devlink_private->efx;
625+
626+
return efx_reflash_flash_firmware(efx, params->fw, extack);
627+
}
628+
618629
static const struct devlink_ops sfc_devlink_ops = {
630+
.supported_flash_update_params = 0,
631+
.flash_update = efx_devlink_flash_update,
619632
.info_get = efx_devlink_info_get,
620633
};
621634

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/****************************************************************************
3+
* Driver for AMD network controllers and boards
4+
* Copyright (C) 2025, Advanced Micro Devices, Inc.
5+
*
6+
* This program is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 as published
8+
* by the Free Software Foundation, incorporated herein by reference.
9+
*/
10+
11+
#include <linux/crc32.h>
12+
#include <net/devlink.h>
13+
#include "efx_reflash.h"
14+
#include "net_driver.h"
15+
#include "fw_formats.h"
16+
#include "mcdi_pcol.h"
17+
#include "mcdi.h"
18+
19+
/* Try to parse a Reflash header at the specified offset */
20+
static bool efx_reflash_parse_reflash_header(const struct firmware *fw,
21+
size_t header_offset, u32 *type,
22+
u32 *subtype, const u8 **data,
23+
size_t *data_size)
24+
{
25+
size_t header_end, trailer_offset, trailer_end;
26+
u32 magic, version, payload_size, header_len;
27+
const u8 *header, *trailer;
28+
u32 expected_crc, crc;
29+
30+
if (check_add_overflow(header_offset, EFX_REFLASH_HEADER_LENGTH_OFST +
31+
EFX_REFLASH_HEADER_LENGTH_LEN,
32+
&header_end))
33+
return false;
34+
if (fw->size < header_end)
35+
return false;
36+
37+
header = fw->data + header_offset;
38+
magic = get_unaligned_le32(header + EFX_REFLASH_HEADER_MAGIC_OFST);
39+
if (magic != EFX_REFLASH_HEADER_MAGIC_VALUE)
40+
return false;
41+
42+
version = get_unaligned_le32(header + EFX_REFLASH_HEADER_VERSION_OFST);
43+
if (version != EFX_REFLASH_HEADER_VERSION_VALUE)
44+
return false;
45+
46+
payload_size = get_unaligned_le32(header + EFX_REFLASH_HEADER_PAYLOAD_SIZE_OFST);
47+
header_len = get_unaligned_le32(header + EFX_REFLASH_HEADER_LENGTH_OFST);
48+
if (check_add_overflow(header_offset, header_len, &trailer_offset) ||
49+
check_add_overflow(trailer_offset, payload_size, &trailer_offset) ||
50+
check_add_overflow(trailer_offset, EFX_REFLASH_TRAILER_LEN,
51+
&trailer_end))
52+
return false;
53+
if (fw->size < trailer_end)
54+
return false;
55+
56+
trailer = fw->data + trailer_offset;
57+
expected_crc = get_unaligned_le32(trailer + EFX_REFLASH_TRAILER_CRC_OFST);
58+
/* Addition could overflow u32, but not size_t since we already
59+
* checked trailer_offset didn't overflow. So cast to size_t first.
60+
*/
61+
crc = crc32_le(0, header, (size_t)header_len + payload_size);
62+
if (crc != expected_crc)
63+
return false;
64+
65+
*type = get_unaligned_le32(header + EFX_REFLASH_HEADER_FIRMWARE_TYPE_OFST);
66+
*subtype = get_unaligned_le32(header + EFX_REFLASH_HEADER_FIRMWARE_SUBTYPE_OFST);
67+
if (*type == EFX_REFLASH_FIRMWARE_TYPE_BUNDLE) {
68+
/* All the bundle data is written verbatim to NVRAM */
69+
*data = fw->data;
70+
*data_size = fw->size;
71+
} else {
72+
/* Other payload types strip the reflash header and trailer
73+
* from the data written to NVRAM
74+
*/
75+
*data = header + header_len;
76+
*data_size = payload_size;
77+
}
78+
79+
return true;
80+
}
81+
82+
/* Map from FIRMWARE_TYPE to NVRAM_PARTITION_TYPE */
83+
static int efx_reflash_partition_type(u32 type, u32 subtype,
84+
u32 *partition_type,
85+
u32 *partition_subtype)
86+
{
87+
int rc = 0;
88+
89+
switch (type) {
90+
case EFX_REFLASH_FIRMWARE_TYPE_BOOTROM:
91+
*partition_type = NVRAM_PARTITION_TYPE_EXPANSION_ROM;
92+
*partition_subtype = subtype;
93+
break;
94+
case EFX_REFLASH_FIRMWARE_TYPE_BUNDLE:
95+
*partition_type = NVRAM_PARTITION_TYPE_BUNDLE;
96+
*partition_subtype = subtype;
97+
break;
98+
default:
99+
/* Not supported */
100+
rc = -EINVAL;
101+
}
102+
103+
return rc;
104+
}
105+
106+
/* Try to parse a SmartNIC image header at the specified offset */
107+
static bool efx_reflash_parse_snic_header(const struct firmware *fw,
108+
size_t header_offset,
109+
u32 *partition_type,
110+
u32 *partition_subtype,
111+
const u8 **data, size_t *data_size)
112+
{
113+
u32 magic, version, payload_size, header_len, expected_crc, crc;
114+
size_t header_end, payload_end;
115+
const u8 *header;
116+
117+
if (check_add_overflow(header_offset, EFX_SNICIMAGE_HEADER_MINLEN,
118+
&header_end) ||
119+
fw->size < header_end)
120+
return false;
121+
122+
header = fw->data + header_offset;
123+
magic = get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_MAGIC_OFST);
124+
if (magic != EFX_SNICIMAGE_HEADER_MAGIC_VALUE)
125+
return false;
126+
127+
version = get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_VERSION_OFST);
128+
if (version != EFX_SNICIMAGE_HEADER_VERSION_VALUE)
129+
return false;
130+
131+
header_len = get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_LENGTH_OFST);
132+
if (check_add_overflow(header_offset, header_len, &header_end))
133+
return false;
134+
payload_size = get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_PAYLOAD_SIZE_OFST);
135+
if (check_add_overflow(header_end, payload_size, &payload_end) ||
136+
fw->size < payload_end)
137+
return false;
138+
139+
expected_crc = get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_CRC_OFST);
140+
141+
/* Calculate CRC omitting the expected CRC field itself */
142+
crc = crc32_le(~0, header, EFX_SNICIMAGE_HEADER_CRC_OFST);
143+
crc = ~crc32_le(crc,
144+
header + EFX_SNICIMAGE_HEADER_CRC_OFST +
145+
EFX_SNICIMAGE_HEADER_CRC_LEN,
146+
header_len + payload_size - EFX_SNICIMAGE_HEADER_CRC_OFST -
147+
EFX_SNICIMAGE_HEADER_CRC_LEN);
148+
if (crc != expected_crc)
149+
return false;
150+
151+
*partition_type =
152+
get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_PARTITION_TYPE_OFST);
153+
*partition_subtype =
154+
get_unaligned_le32(header + EFX_SNICIMAGE_HEADER_PARTITION_SUBTYPE_OFST);
155+
*data = fw->data;
156+
*data_size = fw->size;
157+
return true;
158+
}
159+
160+
/* Try to parse a SmartNIC bundle header at the specified offset */
161+
static bool efx_reflash_parse_snic_bundle_header(const struct firmware *fw,
162+
size_t header_offset,
163+
u32 *partition_type,
164+
u32 *partition_subtype,
165+
const u8 **data,
166+
size_t *data_size)
167+
{
168+
u32 magic, version, bundle_type, header_len, expected_crc, crc;
169+
size_t header_end;
170+
const u8 *header;
171+
172+
if (check_add_overflow(header_offset, EFX_SNICBUNDLE_HEADER_LEN,
173+
&header_end))
174+
return false;
175+
if (fw->size < header_end)
176+
return false;
177+
178+
header = fw->data + header_offset;
179+
magic = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_MAGIC_OFST);
180+
if (magic != EFX_SNICBUNDLE_HEADER_MAGIC_VALUE)
181+
return false;
182+
183+
version = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_VERSION_OFST);
184+
if (version != EFX_SNICBUNDLE_HEADER_VERSION_VALUE)
185+
return false;
186+
187+
bundle_type = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_BUNDLE_TYPE_OFST);
188+
if (bundle_type != NVRAM_PARTITION_TYPE_BUNDLE)
189+
return false;
190+
191+
header_len = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_LENGTH_OFST);
192+
if (header_len != EFX_SNICBUNDLE_HEADER_LEN)
193+
return false;
194+
195+
expected_crc = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_CRC_OFST);
196+
crc = ~crc32_le(~0, header, EFX_SNICBUNDLE_HEADER_CRC_OFST);
197+
if (crc != expected_crc)
198+
return false;
199+
200+
*partition_type = NVRAM_PARTITION_TYPE_BUNDLE;
201+
*partition_subtype = get_unaligned_le32(header + EFX_SNICBUNDLE_HEADER_BUNDLE_SUBTYPE_OFST);
202+
*data = fw->data;
203+
*data_size = fw->size;
204+
return true;
205+
}
206+
207+
/* Try to find a valid firmware payload in the firmware data.
208+
* When we recognise a valid header, we parse it for the partition type
209+
* (so we know where to ask the MC to write it to) and the location of
210+
* the data blob to write.
211+
*/
212+
static int efx_reflash_parse_firmware_data(const struct firmware *fw,
213+
u32 *partition_type,
214+
u32 *partition_subtype,
215+
const u8 **data, size_t *data_size)
216+
{
217+
size_t header_offset;
218+
u32 type, subtype;
219+
220+
/* Some packaging formats (such as CMS/PKCS#7 signed images)
221+
* prepend a header for which finding the size is a non-trivial
222+
* task, so step through the firmware data until we find a valid
223+
* header.
224+
*
225+
* The checks are intended to reject firmware data that is clearly not
226+
* in the expected format. They do not need to be exhaustive as the
227+
* running firmware will perform its own comprehensive validity and
228+
* compatibility checks during the update procedure.
229+
*
230+
* Firmware packages may contain multiple reflash images, e.g. a
231+
* bundle containing one or more other images. Only check the
232+
* outermost container by stopping after the first candidate image
233+
* found even it is for an unsupported partition type.
234+
*/
235+
for (header_offset = 0; header_offset < fw->size; header_offset++) {
236+
if (efx_reflash_parse_snic_bundle_header(fw, header_offset,
237+
partition_type,
238+
partition_subtype,
239+
data, data_size))
240+
return 0;
241+
242+
if (efx_reflash_parse_snic_header(fw, header_offset,
243+
partition_type,
244+
partition_subtype, data,
245+
data_size))
246+
return 0;
247+
248+
if (efx_reflash_parse_reflash_header(fw, header_offset, &type,
249+
&subtype, data, data_size))
250+
return efx_reflash_partition_type(type, subtype,
251+
partition_type,
252+
partition_subtype);
253+
}
254+
255+
return -EINVAL;
256+
}
257+
258+
int efx_reflash_flash_firmware(struct efx_nic *efx, const struct firmware *fw,
259+
struct netlink_ext_ack *extack)
260+
{
261+
struct devlink *devlink = efx->devlink;
262+
u32 type, data_subtype;
263+
size_t data_size;
264+
const u8 *data;
265+
int rc;
266+
267+
if (!efx_has_cap(efx, BUNDLE_UPDATE)) {
268+
NL_SET_ERR_MSG_MOD(extack, "NVRAM bundle updates are not supported by the firmware");
269+
return -EOPNOTSUPP;
270+
}
271+
272+
devlink_flash_update_status_notify(devlink, "Checking update", NULL, 0, 0);
273+
274+
rc = efx_reflash_parse_firmware_data(fw, &type, &data_subtype, &data,
275+
&data_size);
276+
if (rc) {
277+
NL_SET_ERR_MSG_MOD(extack,
278+
"Firmware image validation check failed");
279+
goto out;
280+
}
281+
282+
rc = -EOPNOTSUPP;
283+
284+
out:
285+
devlink_flash_update_status_notify(devlink, rc ? "Update failed" :
286+
"Update complete",
287+
NULL, 0, 0);
288+
return rc;
289+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/****************************************************************************
3+
* Driver for AMD network controllers and boards
4+
* Copyright (C) 2025, Advanced Micro Devices, Inc.
5+
*
6+
* This program is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 as published
8+
* by the Free Software Foundation, incorporated herein by reference.
9+
*/
10+
11+
#ifndef _EFX_REFLASH_H
12+
#define _EFX_REFLASH_H
13+
14+
#include "net_driver.h"
15+
#include <linux/firmware.h>
16+
17+
int efx_reflash_flash_firmware(struct efx_nic *efx, const struct firmware *fw,
18+
struct netlink_ext_ack *extack);
19+
20+
#endif /* _EFX_REFLASH_H */

0 commit comments

Comments
 (0)