DPDK patches and discussions
 help / color / mirror / Atom feed
* [PATCH 0/3] reload the firmware as needed
@ 2024-02-27 11:15 Chaoyong He
  2024-02-27 11:15 ` [PATCH 1/3] net/nfp: add the elf module Chaoyong He
                   ` (3 more replies)
  0 siblings, 4 replies; 17+ messages in thread
From: Chaoyong He @ 2024-02-27 11:15 UTC (permalink / raw)
  To: dev; +Cc: oss-drivers, Chaoyong He

Add the necessary logic to get firmware version from firmware file, and
only reload the firmware when the firmware version changed.

Also add a device argument which can force reload the firmware and
ignore the firmware version.

Peng Zhang (3):
  net/nfp: add the elf module
  net/nfp: reload the firmware only when firmware changed
  net/nfp: add force reload firmware option

 drivers/net/nfp/meson.build       |    1 +
 drivers/net/nfp/nfp_ethdev.c      |  177 ++++-
 drivers/net/nfp/nfp_net_common.c  |   17 +
 drivers/net/nfp/nfp_net_common.h  |   10 +
 drivers/net/nfp/nfpcore/nfp_elf.c | 1079 +++++++++++++++++++++++++++++
 drivers/net/nfp/nfpcore/nfp_elf.h |   13 +
 drivers/net/nfp/nfpcore/nfp_mip.c |   30 +-
 drivers/net/nfp/nfpcore/nfp_mip.h |   70 +-
 8 files changed, 1355 insertions(+), 42 deletions(-)
 create mode 100644 drivers/net/nfp/nfpcore/nfp_elf.c
 create mode 100644 drivers/net/nfp/nfpcore/nfp_elf.h

-- 
2.39.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/3] net/nfp: add the elf module
  2024-02-27 11:15 [PATCH 0/3] reload the firmware as needed Chaoyong He
@ 2024-02-27 11:15 ` Chaoyong He
  2024-02-28 16:50   ` Ferruh Yigit
  2024-02-28 22:18   ` Stephen Hemminger
  2024-02-27 11:15 ` [PATCH 2/3] net/nfp: reload the firmware only when firmware changed Chaoyong He
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 17+ messages in thread
From: Chaoyong He @ 2024-02-27 11:15 UTC (permalink / raw)
  To: dev; +Cc: oss-drivers, Peng Zhang, Chaoyong He, Long Wu

From: Peng Zhang <peng.zhang@corigine.com>

Add the elf module, which can get mip information from the
firmware ELF file.

Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
Reviewed-by: Long Wu <long.wu@corigine.com>
---
 drivers/net/nfp/meson.build       |    1 +
 drivers/net/nfp/nfpcore/nfp_elf.c | 1079 +++++++++++++++++++++++++++++
 drivers/net/nfp/nfpcore/nfp_elf.h |   13 +
 drivers/net/nfp/nfpcore/nfp_mip.c |   30 +-
 drivers/net/nfp/nfpcore/nfp_mip.h |   70 +-
 5 files changed, 1168 insertions(+), 25 deletions(-)
 create mode 100644 drivers/net/nfp/nfpcore/nfp_elf.c
 create mode 100644 drivers/net/nfp/nfpcore/nfp_elf.h

diff --git a/drivers/net/nfp/meson.build b/drivers/net/nfp/meson.build
index e376fd328f..959ca01844 100644
--- a/drivers/net/nfp/meson.build
+++ b/drivers/net/nfp/meson.build
@@ -18,6 +18,7 @@ sources = files(
         'nfdk/nfp_nfdk_dp.c',
         'nfpcore/nfp_cppcore.c',
         'nfpcore/nfp_crc.c',
+        'nfpcore/nfp_elf.c',
         'nfpcore/nfp_hwinfo.c',
         'nfpcore/nfp_mip.c',
         'nfpcore/nfp_mutex.c',
diff --git a/drivers/net/nfp/nfpcore/nfp_elf.c b/drivers/net/nfp/nfpcore/nfp_elf.c
new file mode 100644
index 0000000000..fbd350589b
--- /dev/null
+++ b/drivers/net/nfp/nfpcore/nfp_elf.c
@@ -0,0 +1,1079 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Corigine, Inc.
+ * All rights reserved.
+ */
+
+#include "nfp_elf.h"
+
+#include <malloc.h>
+#include <stdbool.h>
+#include <ethdev_pci.h>
+
+#include <nfp_platform.h>
+#include <rte_common.h>
+#include <eal_firmware.h>
+
+#include "nfp_logs.h"
+#include "nfp_mip.h"
+
+/*
+ * NFP Chip Families.
+ *
+ * These are not enums, because they need to be microcode compatible.
+ * They are also not maskable.
+ *
+ * Note: The NFP-4xxx family is handled as NFP-6xxx in most software
+ * components.
+ */
+#define NFP_CHIP_FAMILY_NFP3800 0x3800
+#define NFP_CHIP_FAMILY_NFP6000 0x6000
+
+/* Standard ELF */
+#define NFP_ELF_EI_NIDENT     16
+#define NFP_ELF_EI_MAG0       0
+#define NFP_ELF_EI_MAG1       1
+#define NFP_ELF_EI_MAG2       2
+#define NFP_ELF_EI_MAG3       3
+#define NFP_ELF_EI_CLASS      4
+#define NFP_ELF_EI_DATA       5
+#define NFP_ELF_EI_VERSION    6
+#define NFP_ELF_EI_PAD        7
+#define NFP_ELF_ELFMAG0       0x7f
+#define NFP_ELF_ELFMAG1       'E'
+#define NFP_ELF_ELFMAG2       'L'
+#define NFP_ELF_ELFMAG3       'F'
+#define NFP_ELF_ELFCLASSNONE  0
+#define NFP_ELF_ELFCLASS32    1
+#define NFP_ELF_ELFCLASS64    2
+#define NFP_ELF_ELFDATANONE   0
+#define NFP_ELF_ELFDATA2LSB   1
+#define NFP_ELF_ELFDATA2MSB   2
+
+#define NFP_ELF_ET_NONE       0
+#define NFP_ELF_ET_REL        1
+#define NFP_ELF_ET_EXEC       2
+#define NFP_ELF_ET_DYN        3
+#define NFP_ELF_ET_CORE       4
+#define NFP_ELF_ET_LOPROC     0xFF00
+#define NFP_ELF_ET_HIPROC     0xFFFF
+#define NFP_ELF_ET_NFP_PARTIAL_REL   (NFP_ELF_ET_LOPROC + NFP_ELF_ET_REL)
+#define NFP_ELF_ET_NFP_PARTIAL_EXEC  (NFP_ELF_ET_LOPROC + NFP_ELF_ET_EXEC)
+
+#define NFP_ELF_EM_NFP        250
+#define NFP_ELF_EM_NFP6000    0x6000
+
+#define NFP_ELF_SHT_NULL      0
+#define NFP_ELF_SHT_PROGBITS  1
+#define NFP_ELF_SHT_SYMTAB    2
+#define NFP_ELF_SHT_STRTAB    3
+#define NFP_ELF_SHT_RELA      4
+#define NFP_ELF_SHT_HASH      5
+#define NFP_ELF_SHT_DYNAMIC   6
+#define NFP_ELF_SHT_NOTE      7
+#define NFP_ELF_SHT_NOBITS    8
+#define NFP_ELF_SHT_REL       9
+#define NFP_ELF_SHT_SHLIB     10
+#define NFP_ELF_SHT_DYNSYM    11
+#define NFP_ELF_SHT_LOPROC    0x70000000
+#define NFP_ELF_SHT_HIPROC    0x7fffffff
+#define NFP_ELF_SHT_LOUSER    0x80000000
+#define NFP_ELF_SHT_HIUSER    0x8fffffff
+
+#define NFP_ELF_EV_NONE       0
+#define NFP_ELF_EV_CURRENT    1
+
+#define NFP_ELF_SHN_UNDEF     0
+
+/* EM_NFP ELF flags */
+
+/*
+ * Valid values for FAMILY are:
+ * 0x6000 - NFP-6xxx/NFP-4xxx
+ * 0x3800 - NFP-38xx
+ */
+#define NFP_ELF_EF_NFP_FAMILY_MASK        0xFFFF
+#define NFP_ELF_EF_NFP_FAMILY_LSB         8
+
+#define NFP_ELF_SHT_NFP_MECONFIG          (NFP_ELF_SHT_LOPROC + 1)
+#define NFP_ELF_SHT_NFP_INITREG           (NFP_ELF_SHT_LOPROC + 2)
+#define NFP_ELF_SHT_UOF_DEBUG             (NFP_ELF_SHT_LOUSER)
+
+/* NFP target revision note type */
+#define NFP_ELT_NOTE_NAME_NFP             "NFP\0"
+#define NFP_ELT_NOTE_NAME_NFP_SZ          4
+#define NFP_ELT_NOTE_NAME_NFP_USER        "NFP_USR\0"
+#define NFP_ELT_NOTE_NAME_NFP_USER_SZ     8
+#define NFP_ELF_NT_NFP_BUILD_INFO         0x100
+#define NFP_ELF_NT_NFP_REVS               0x101
+#define NFP_ELF_NT_NFP_MIP_LOCATION       0x102
+#define NFP_ELF_NT_NFP_USER               0xf0000000
+
+
+/* Standard ELF structures */
+struct nfp_elf_elf64_ehdr {
+	uint8_t e_ident[NFP_ELF_EI_NIDENT];
+	rte_le16_t e_type;
+	rte_le16_t e_machine;
+	rte_le32_t e_version;
+	rte_le64_t e_entry;
+	rte_le64_t e_phoff;
+	rte_le64_t e_shoff;
+	rte_le32_t e_flags;
+	rte_le16_t e_ehsize;
+	rte_le16_t e_phentsize;
+	rte_le16_t e_phnum;
+	rte_le16_t e_shentsize;
+	rte_le16_t e_shnum;
+	rte_le16_t e_shstrndx;
+};
+
+struct nfp_elf_elf64_shdr {
+	rte_le32_t sh_name;
+	rte_le32_t sh_type;
+	rte_le64_t sh_flags;
+	rte_le64_t sh_addr;
+	rte_le64_t sh_offset;
+	rte_le64_t sh_size;
+	rte_le32_t sh_link;
+	rte_le32_t sh_info;
+	rte_le64_t sh_addralign;
+	rte_le64_t sh_entsize;
+};
+
+struct nfp_elf_elf64_sym {
+	rte_le32_t st_name;
+	uint8_t st_info;
+	uint8_t st_other;
+	rte_le16_t st_shndx;
+	rte_le64_t st_value;
+	rte_le64_t st_size;
+};
+
+struct nfp_elf_elf64_rel {
+	rte_le64_t r_offset;
+	rte_le64_t r_info;
+};
+
+struct nfp_elf_elf64_nhdr {
+	rte_le32_t n_namesz;
+	rte_le32_t n_descsz;
+	rte_le32_t n_type;
+};
+
+/* NFP specific structures */
+struct nfp_elf_elf_meconfig {
+	rte_le32_t ctx_enables;
+	rte_le32_t entry;
+	rte_le32_t misc_control;
+	rte_le32_t reserved;
+};
+
+struct nfp_elf_elf_initregentry {
+	rte_le32_t w0;
+	rte_le32_t cpp_offset_lo;
+	rte_le32_t val;
+	rte_le32_t mask;
+};
+
+/* NFP NFFW ELF struct and API */
+struct nfp_elf_user_note {
+	const char *name;
+	uint32_t data_sz;
+	void *data;
+};
+
+/*
+ * nfp_elf_fw_mip contains firmware related fields from the MIP as well as the
+ * MIP location in the NFFW file. All fields are only valid if shndx > 0.
+ *
+ * This struct will only be available if the firmware contains a .note section
+ * with a note of type NFP_ELF_NT_NFP_MIP_LOCATION.
+ */
+struct nfp_elf_fw_mip {
+	size_t shndx;
+	uint64_t sh_offset;
+	rte_le32_t mip_ver;      /**< Version of the format of the MIP itself */
+
+	rte_le32_t fw_version;
+	rte_le32_t fw_buildnum;
+	rte_le32_t fw_buildtime;
+	char fw_name[20];        /**< At most 16 chars, 17 ensures '\0', round up */
+	const char *fw_typeid;   /**< NULL if none set */
+};
+
+/*
+ * It is preferred to access this struct via the nfp_elf functions
+ * rather than directly.
+ */
+struct nfp_elf {
+	struct nfp_elf_elf64_ehdr *ehdr;
+	struct nfp_elf_elf64_shdr *shdrs;
+	size_t shdrs_cnt;
+	void **shdrs_data;
+
+	/** True if section data has been endian swapped */
+	uint8_t *shdrs_host_endian;
+
+	size_t shdr_idx_symtab;
+
+	struct nfp_elf_elf64_sym *syms;
+	size_t syms_cnt;
+
+	char *shstrtab;
+	size_t shstrtab_sz;
+
+	char *symstrtab;
+	size_t symstrtab_sz;
+
+	struct nfp_elf_elf_meconfig *meconfs;
+	size_t meconfs_cnt;
+
+	/* ==== .note data start ==== */
+
+	/**
+	 * Following data derived from SHT_NOTE sections for read-only usage.
+	 * These fields are not used in nfp_elf_to_buf()
+	 */
+	int rev_min; /**< -1 if file did not specify */
+	int rev_max; /**< -1 if file did not specify */
+
+	/**
+	 * If mip_shndx == 0 and mip_sh_off == 0, the .note stated there is no MIP.
+	 * If mip_shndx == 0 and mip_sh_off == UINT64_MAX, there was no .note and
+	 * a MIP _may_ still be found in the first 256KiB of DRAM/EMEM data.
+	 */
+	size_t mip_shndx; /**< Section in which MIP resides, 0 if no MIP */
+	uint64_t mip_sh_off; /**< Offset within section (not address) */
+
+	struct nfp_elf_fw_mip fw_mip;
+	const char *fw_info_strtab;
+	size_t fw_info_strtab_sz;
+
+	/* ==== .note.user data start ==== */
+	size_t user_note_cnt;
+	struct nfp_elf_user_note *user_notes;
+
+	void *dbgdata;
+
+	int family;
+
+	/**
+	 * For const entry points in the API, we allocate and keep a buffer
+	 * and for mutable entry points we assume the buffer remains valid
+	 * and we just set pointers to it.
+	 */
+	void *_buf;
+	size_t _bufsz;
+};
+
+static void
+nfp_elf_free(struct nfp_elf *ectx)
+{
+	if (ectx == NULL)
+		return;
+
+	free(ectx->shdrs);
+	free(ectx->shdrs_data);
+	free(ectx->shdrs_host_endian);
+	if (ectx->_bufsz != 0)
+		free(ectx->_buf);
+
+	free(ectx);
+}
+
+static size_t
+nfp_elf_get_sec_ent_cnt(struct nfp_elf *ectx,
+		size_t idx)
+{
+	uint64_t sh_size = rte_le_to_cpu_64(ectx->shdrs[idx].sh_size);
+	uint64_t sh_entsize = rte_le_to_cpu_64(ectx->shdrs[idx].sh_entsize);
+
+	if (sh_entsize != 0)
+		return sh_size / sh_entsize;
+
+	return 0;
+}
+
+static bool
+nfp_elf_check_sh_size(uint64_t sh_size)
+{
+	if (sh_size == 0 || sh_size > UINT32_MAX)
+		return false;
+
+	return true;
+}
+
+static const char *
+nfp_elf_fwinfo_next(struct nfp_elf *ectx,
+		const char *key_val)
+{
+	size_t s_len;
+	const char *strtab = ectx->fw_info_strtab;
+	ssize_t tab_sz = (ssize_t)ectx->fw_info_strtab_sz;
+
+	if (key_val == NULL)
+		return strtab;
+
+	s_len = strlen(key_val);
+	if (key_val < strtab || ((key_val + s_len + 1) >= (strtab + tab_sz - 1)))
+		return NULL;
+
+	key_val += s_len + 1;
+
+	return key_val;
+}
+
+static const char *
+nfp_elf_fwinfo_lookup(struct nfp_elf *ectx,
+		const char *key)
+{
+	size_t s_len;
+	const char *s;
+	size_t key_len = strlen(key);
+	const char *strtab = ectx->fw_info_strtab;
+	ssize_t tab_sz = (ssize_t)ectx->fw_info_strtab_sz;
+
+	if (strtab == NULL)
+		return NULL;
+
+	for (s = strtab, s_len = strlen(s) + 1;
+			(s[0] != '\0') && (tab_sz > 0);
+			s_len = strlen(s) + 1, tab_sz -= s_len, s += s_len) {
+		if ((strncmp(s, key, key_len) == 0) && (s[key_len] == '='))
+			return &s[key_len + 1];
+	}
+
+	return NULL;
+}
+
+static bool
+nfp_elf_arch_is_thornham(struct nfp_elf *ectx)
+{
+	if (ectx == NULL)
+		return false;
+
+	if (ectx->family == NFP_CHIP_FAMILY_NFP6000 || ectx->family == NFP_CHIP_FAMILY_NFP3800)
+		return true;
+
+	return false;
+}
+
+static int
+nfp_elf_parse_sht_rel(struct nfp_elf_elf64_shdr *sec,
+		struct nfp_elf *ectx,
+		size_t idx,
+		uint8_t *buf8)
+{
+	uint64_t sh_size = rte_le_to_cpu_64(sec->sh_size);
+	uint64_t sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+	uint64_t sh_entsize = rte_le_to_cpu_64(sec->sh_entsize);
+
+	if (sh_entsize != sizeof(struct nfp_elf_elf64_rel)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	if (!nfp_elf_check_sh_size(sh_size)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	ectx->shdrs_data[idx] = buf8 + sh_offset;
+	ectx->shdrs_host_endian[idx] = 1;
+
+	return 0;
+}
+
+static int
+nfp_elf_parse_note_name_nfp(struct nfp_elf *ectx,
+		size_t idx,
+		uint32_t ndescsz,
+		uint32_t ntype,
+		const char *nname,
+		rte_le32_t *descword)
+{
+	if (strncmp(nname, NFP_ELT_NOTE_NAME_NFP, NFP_ELT_NOTE_NAME_NFP_SZ) == 0) {
+		switch (ntype) {
+		case NFP_ELF_NT_NFP_REVS:
+			if (ndescsz != 8) {
+				PMD_DRV_LOG(ERR, "Invalid ELF NOTE descsz in section %zu.", idx);
+				return -EINVAL;
+			}
+
+			ectx->rev_min = (int)rte_le_to_cpu_32(descword[0]);
+			ectx->rev_max = (int)rte_le_to_cpu_32(descword[1]);
+			break;
+		case NFP_ELF_NT_NFP_MIP_LOCATION:
+			if (ndescsz != 12) {
+				PMD_DRV_LOG(ERR, "Invalid ELF NOTE descsz in section %zu.", idx);
+				return -EINVAL;
+			}
+
+			ectx->mip_shndx = rte_le_to_cpu_32(descword[0]);
+			if (ectx->mip_shndx == 0) {
+				ectx->mip_sh_off = 0;
+				break;
+			}
+
+			if (ectx->mip_shndx >= ectx->shdrs_cnt) {
+				PMD_DRV_LOG(ERR, "Invalid ELF NOTE shndx in section %zu.", idx);
+				return -EINVAL;
+			}
+
+			ectx->mip_sh_off = rte_le_to_cpu_32(descword[1]) |
+					(uint64_t)rte_le_to_cpu_32(descword[2]) << 32;
+			break;
+		default:
+			break;
+		}
+	} else if (strncmp(nname, NFP_ELT_NOTE_NAME_NFP_USER,
+			NFP_ELT_NOTE_NAME_NFP_USER_SZ) == 0 && ntype == NFP_ELF_NT_NFP_USER) {
+		ectx->user_note_cnt++;
+	}
+
+	return 0;
+}
+
+static int
+nfp_elf_parse_sht_note(struct nfp_elf_elf64_shdr *sec,
+		struct nfp_elf *ectx,
+		size_t idx,
+		uint8_t *buf8)
+{
+	int err;
+	size_t nsz;
+	uint8_t *desc;
+	uint32_t ntype;
+	uint32_t nnamesz;
+	uint32_t ndescsz;
+	const char *nname;
+	uint8_t *shdrs_data;
+	rte_le32_t *descword;
+	struct nfp_elf_elf64_nhdr *nhdr;
+	struct nfp_elf_user_note *unote;
+	uint64_t sh_size = rte_le_to_cpu_64(sec->sh_size);
+	uint64_t sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+
+	if (!nfp_elf_check_sh_size(sh_size)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	shdrs_data = buf8 + sh_offset;
+	ectx->shdrs_data[idx] = shdrs_data;
+	ectx->shdrs_host_endian[idx] = 0;
+
+	/* Extract notes that we recognise */
+	nhdr = (struct nfp_elf_elf64_nhdr *)shdrs_data;
+
+	while ((uint8_t *)nhdr < (shdrs_data + sh_size)) {
+		nnamesz  = rte_le_to_cpu_32(nhdr->n_namesz);
+		ndescsz  = rte_le_to_cpu_32(nhdr->n_descsz);
+		ntype    = rte_le_to_cpu_32(nhdr->n_type);
+		nname    = (const char *)((uint8_t *)nhdr + sizeof(*nhdr));
+		descword = (rte_le32_t *)((uint8_t *)nhdr + sizeof(*nhdr) +
+				((nnamesz + UINT32_C(3)) & ~UINT32_C(3)));
+
+		err = nfp_elf_parse_note_name_nfp(ectx, idx, ndescsz, ntype, nname, descword);
+		if (err != 0)
+			return err;
+
+		nhdr = (struct nfp_elf_elf64_nhdr *)((uint8_t *)descword +
+				((ndescsz + UINT32_C(3)) & ~UINT32_C(3)));
+	}
+
+	if (ectx->user_note_cnt == 0)
+		return 0;
+
+	ectx->user_notes = calloc(ectx->user_note_cnt, sizeof(*ectx->user_notes));
+	if (ectx->user_notes == NULL) {
+		PMD_DRV_LOG(ERR, "Out of memory.");
+		return -ENOMEM;
+	}
+
+	nhdr = (struct nfp_elf_elf64_nhdr *)shdrs_data;
+	unote = ectx->user_notes;
+	while ((uint8_t *)nhdr < (shdrs_data + sh_size)) {
+		nnamesz = rte_le_to_cpu_32(nhdr->n_namesz);
+		ndescsz = rte_le_to_cpu_32(nhdr->n_descsz);
+		ntype   = rte_le_to_cpu_32(nhdr->n_type);
+		nname   = (const char *)((uint8_t *)nhdr + sizeof(*nhdr));
+		desc    = (uint8_t *)nhdr + sizeof(*nhdr) +
+				((nnamesz + UINT32_C(3)) & ~UINT32_C(3));
+
+		if (strncmp(nname, NFP_ELT_NOTE_NAME_NFP_USER,
+				NFP_ELT_NOTE_NAME_NFP_USER_SZ) != 0)
+			continue;
+
+		if (ntype != NFP_ELF_NT_NFP_USER)
+			continue;
+
+		unote->name = (const char *)desc;
+		nsz = strlen(unote->name) + 1;
+		if (nsz % 4 != 0)
+			nsz = ((nsz / 4) + 1) * 4;
+		if (nsz > ndescsz) {
+			PMD_DRV_LOG(ERR, "Invalid ELF USER NOTE descsz in section %zu.", idx);
+			return -EINVAL;
+		}
+
+		unote->data_sz = ndescsz - (uint32_t)nsz;
+		if (unote->data_sz != 0)
+			unote->data = desc + nsz;
+		unote++;
+
+		nhdr = (struct nfp_elf_elf64_nhdr *)
+				(desc + ((ndescsz + UINT32_C(3)) & ~UINT32_C(3)));
+	}
+
+	return 0;
+}
+
+static int
+nfp_elf_parse_sht_meconfig(struct nfp_elf_elf64_shdr *sec,
+		struct nfp_elf *ectx,
+		size_t idx,
+		uint8_t *buf8)
+{
+	size_t ent_cnt;
+	uint8_t *shdrs_data;
+	uint64_t sh_size = rte_le_to_cpu_64(sec->sh_size);
+	uint64_t sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+
+	if (!nfp_elf_check_sh_size(sh_size)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	shdrs_data = buf8 + sh_offset;
+	ent_cnt = nfp_elf_get_sec_ent_cnt(ectx, idx);
+	ectx->shdrs_data[idx] = shdrs_data;
+	ectx->meconfs = (struct nfp_elf_elf_meconfig *)shdrs_data;
+	ectx->meconfs_cnt = ent_cnt;
+	ectx->shdrs_host_endian[idx] = 1;
+
+	return 0;
+}
+
+static int
+nfp_elf_parse_sht_initreg(struct nfp_elf_elf64_shdr *sec,
+		struct nfp_elf *ectx,
+		size_t idx,
+		uint8_t *buf8)
+{
+	uint64_t sh_size = rte_le_to_cpu_64(sec->sh_size);
+	uint64_t sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+	uint64_t sh_entsize = rte_le_to_cpu_64(sec->sh_entsize);
+
+	if (!nfp_elf_arch_is_thornham(ectx)) {
+		PMD_DRV_LOG(ERR, "Section not supported for target arch.");
+		return -ENOTSUP;
+	}
+
+	if (sh_entsize != sizeof(struct nfp_elf_elf_initregentry) ||
+			!nfp_elf_check_sh_size(sh_size)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	ectx->shdrs_data[idx] = buf8 + sh_offset;
+	ectx->shdrs_host_endian[idx] = 1;
+
+	return 0;
+}
+
+static int
+nfp_elf_parse_sht_symtab(struct nfp_elf_elf64_shdr *sec,
+		struct nfp_elf *ectx,
+		size_t idx,
+		uint8_t *buf8)
+{
+	uint64_t sh_size = rte_le_to_cpu_64(sec->sh_size);
+	uint64_t sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+	uint64_t sh_entsize = rte_le_to_cpu_64(sec->sh_entsize);
+
+	if (sh_entsize != sizeof(struct nfp_elf_elf64_sym) ||
+			!nfp_elf_check_sh_size(sh_size)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	ectx->shdrs_data[idx] = buf8 + sh_offset;
+	ectx->shdrs_host_endian[ectx->shdr_idx_symtab] = 1;
+
+	return 0;
+}
+
+static int
+nfp_elf_populate_fw_mip(struct nfp_elf *ectx,
+		uint8_t *buf8)
+{
+	uint8_t *pu8;
+	const char *nx;
+	uint64_t sh_size;
+	uint64_t sh_offset;
+	uint32_t first_entry;
+	const struct nfp_mip *mip;
+	struct nfp_elf_elf64_shdr *sec;
+	const struct nfp_mip_entry *ent;
+	const struct nfp_mip_fwinfo_entry *fwinfo;
+
+	sec = &ectx->shdrs[ectx->mip_shndx];
+	sh_size = rte_le_to_cpu_64(sec->sh_size);
+	sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+	pu8 = buf8 + sh_offset + ectx->mip_sh_off;
+	mip = (const struct nfp_mip *)pu8;
+	first_entry = rte_le_to_cpu_32(mip->first_entry);
+
+	if (mip->signature != NFP_MIP_SIGNATURE) {
+		PMD_DRV_LOG(ERR, "Incorrect MIP signature %#08x",
+				rte_le_to_cpu_32(mip->signature));
+		return -EINVAL;
+	}
+
+	ectx->fw_mip.shndx = ectx->mip_shndx;
+	ectx->fw_mip.sh_offset = ectx->mip_sh_off;
+	ectx->fw_mip.mip_ver = mip->mip_version;
+
+	if (ectx->fw_mip.mip_ver != NFP_MIP_VERSION) {
+		PMD_DRV_LOG(ERR, "MIP note pointer does not point to recognised version.");
+		return -EINVAL;
+	}
+
+	ectx->fw_mip.fw_version   = mip->version;
+	ectx->fw_mip.fw_buildnum  = mip->buildnum;
+	ectx->fw_mip.fw_buildtime = mip->buildtime;
+	strncpy(ectx->fw_mip.fw_name, mip->name, 16);
+
+	/*
+	 * If there is a FWINFO v1 entry, it will be first and
+	 * right after the MIP itself, so in the same section.
+	 */
+	if (ectx->mip_sh_off + first_entry + sizeof(*ent) < sh_size) {
+		pu8 += first_entry;
+		ent = (const struct nfp_mip_entry *)pu8;
+		if (ent->type == NFP_MIP_TYPE_FWINFO && ent->version == 1) {
+			pu8 += sizeof(*ent);
+			fwinfo = (const struct nfp_mip_fwinfo_entry *)pu8;
+			if (fwinfo->kv_len != 0) {
+				ectx->fw_info_strtab_sz = fwinfo->kv_len;
+				ectx->fw_info_strtab = fwinfo->key_value_strs;
+			}
+		}
+	}
+
+	ectx->fw_mip.fw_typeid = nfp_elf_fwinfo_lookup(ectx, "TypeId");
+
+	/*
+	 * TypeId will be the last reserved key-value pair, so skip
+	 * to the first entry after it for the user values.
+	 */
+	if (ectx->fw_mip.fw_typeid == NULL)
+		return 0;
+
+	nx = nfp_elf_fwinfo_next(ectx, ectx->fw_mip.fw_typeid);
+	if (nx == NULL)
+		ectx->fw_info_strtab_sz = 0;
+	else
+		ectx->fw_info_strtab_sz -= (nx - ectx->fw_info_strtab);
+	ectx->fw_info_strtab = nx;
+
+	return 0;
+}
+
+static int
+nfp_elf_read_file_headers(struct nfp_elf *ectx,
+		void *buf)
+{
+	uint16_t e_type;
+	uint32_t e_flags;
+	uint32_t e_version;
+	uint16_t e_machine;
+
+	ectx->ehdr = buf;
+	e_type = rte_le_to_cpu_16(ectx->ehdr->e_type);
+	e_flags = rte_le_to_cpu_32(ectx->ehdr->e_flags);
+	e_version = rte_le_to_cpu_32(ectx->ehdr->e_version);
+	e_machine = rte_le_to_cpu_16(ectx->ehdr->e_machine);
+
+	switch (e_machine) {
+	case NFP_ELF_EM_NFP:
+		ectx->family = (e_flags >> NFP_ELF_EF_NFP_FAMILY_LSB)
+				& NFP_ELF_EF_NFP_FAMILY_MASK;
+		break;
+	case NFP_ELF_EM_NFP6000:
+		ectx->family = NFP_CHIP_FAMILY_NFP6000;
+		break;
+	default:
+		PMD_DRV_LOG(ERR, "Invalid ELF machine type.");
+		return -EINVAL;
+	}
+
+	if ((e_type != NFP_ELF_ET_EXEC && e_type != NFP_ELF_ET_REL &&
+			e_type != NFP_ELF_ET_NFP_PARTIAL_EXEC &&
+			e_type != NFP_ELF_ET_NFP_PARTIAL_REL) ||
+			e_version != NFP_ELF_EV_CURRENT ||
+			ectx->ehdr->e_ehsize != sizeof(struct nfp_elf_elf64_ehdr) ||
+			ectx->ehdr->e_shentsize != sizeof(struct nfp_elf_elf64_shdr)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF file header.");
+		return -EINVAL;
+	}
+
+	if (ectx->ehdr->e_shoff < ectx->ehdr->e_ehsize) {
+		PMD_DRV_LOG(ERR, "Invalid ELF header content.");
+		return -EINVAL;
+	}
+
+	if (ectx->ehdr->e_shstrndx >= ectx->ehdr->e_shnum) {
+		PMD_DRV_LOG(ERR, "Invalid ELF header content.");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+nfp_elf_read_section_headers(struct nfp_elf *ectx,
+		uint8_t *buf8,
+		size_t buf_len)
+{
+	size_t idx;
+	int err = 0;
+	uint8_t *pu8;
+	uint64_t sh_size;
+	uint64_t sh_offset;
+	uint64_t sh_entsize;
+	struct nfp_elf_elf64_shdr *sec;
+	uint64_t e_shoff = rte_le_to_cpu_16(ectx->ehdr->e_shoff);
+	uint16_t e_shnum = rte_le_to_cpu_16(ectx->ehdr->e_shnum);
+
+	if (buf_len < e_shoff + ((size_t)e_shnum * sizeof(*sec))) {
+		PMD_DRV_LOG(ERR, "ELF data too short.");
+		return -EINVAL;
+	}
+
+	pu8 = buf8 + e_shoff;
+
+	if (e_shnum == 0) {
+		ectx->shdrs = NULL;
+		ectx->shdrs_data = NULL;
+		ectx->shdrs_host_endian = NULL;
+		ectx->shdrs_cnt = 0;
+		return 0;
+	}
+
+	ectx->shdrs = calloc(e_shnum, sizeof(*ectx->shdrs));
+	if (ectx->shdrs == NULL) {
+		PMD_DRV_LOG(ERR, "Out of memory.");
+		return -ENOMEM;
+	}
+
+	ectx->shdrs_data = calloc(e_shnum, sizeof(void *));
+	if (ectx->shdrs_data == NULL) {
+		PMD_DRV_LOG(ERR, "Out of memory.");
+		err = -ENOMEM;
+		goto free_shdrs;
+	}
+
+	ectx->shdrs_host_endian = calloc(e_shnum, sizeof(ectx->shdrs_host_endian[0]));
+	if (ectx->shdrs_host_endian == NULL) {
+		PMD_DRV_LOG(ERR, "Out of memory.");
+		err = -ENOMEM;
+		goto free_shdrs_data;
+	}
+
+	memcpy(ectx->shdrs, pu8, e_shnum * sizeof(*ectx->shdrs));
+	ectx->shdrs_cnt = e_shnum;
+
+	for (idx = 0, sec = ectx->shdrs; idx < ectx->shdrs_cnt; idx++, sec++) {
+		sh_size = rte_le_to_cpu_64(sec->sh_size);
+		sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+		sh_entsize = rte_le_to_cpu_64(sec->sh_entsize);
+
+		if (sh_entsize != 0 && (sh_size % sh_entsize != 0)) {
+			PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+			err = -EINVAL;
+			goto free_shdrs_host_endian;
+		}
+
+		switch (rte_le_to_cpu_32(sec->sh_type)) {
+		case NFP_ELF_SHT_REL:
+			err = nfp_elf_parse_sht_rel(sec, ectx, idx, buf8);
+			if (err != 0) {
+				PMD_DRV_LOG(ERR, "Failed to parse sht rel.");
+				goto free_shdrs_host_endian;
+			}
+			break;
+		case NFP_ELF_SHT_NOTE:
+			err = nfp_elf_parse_sht_note(sec, ectx, idx, buf8);
+			if (err != 0) {
+				PMD_DRV_LOG(ERR, "Failed to parse sht note.");
+				goto free_shdrs_host_endian;
+			}
+			break;
+		case NFP_ELF_SHT_NFP_MECONFIG:
+			err = nfp_elf_parse_sht_meconfig(sec, ectx, idx, buf8);
+			if (err != 0) {
+				PMD_DRV_LOG(ERR, "Failed to parse sht meconfig.");
+				goto free_shdrs_host_endian;
+			}
+			break;
+		case NFP_ELF_SHT_NFP_INITREG:
+			err = nfp_elf_parse_sht_initreg(sec, ectx, idx, buf8);
+			if (err != 0) {
+				PMD_DRV_LOG(ERR, "Failed to parse sht initregp.");
+				goto free_shdrs_host_endian;
+			}
+			break;
+		case NFP_ELF_SHT_SYMTAB:
+			err = nfp_elf_parse_sht_symtab(sec, ectx, idx, buf8);
+			if (err != 0) {
+				PMD_DRV_LOG(ERR, "Failed to parse sht symtab.");
+				goto free_shdrs_host_endian;
+			}
+			break;
+		case NFP_ELF_SHT_NOBITS:
+		case NFP_ELF_SHT_NULL:
+			break;
+		default:
+			if (sh_offset > 0 && sh_size <= 0)
+				break;
+
+			/*
+			 * Limit sections to 4GiB, because they won't need to be this large
+			 * and this ensures we can handle the file on 32-bit hosts without
+			 * unexpected problems.
+			 */
+			if (sh_size > UINT32_MAX) {
+				PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+				err = -EINVAL;
+				goto free_shdrs_host_endian;
+			}
+
+			pu8 = buf8 + sh_offset;
+			ectx->shdrs_data[idx] = pu8;
+			ectx->shdrs_host_endian[idx] = 0;
+			break;
+		}
+	}
+
+	return 0;
+
+free_shdrs_host_endian:
+	free(ectx->shdrs_host_endian);
+free_shdrs_data:
+	free(ectx->shdrs_data);
+free_shdrs:
+	free(ectx->shdrs);
+
+	return err;
+}
+
+static int
+nfp_elf_read_shstrtab(struct nfp_elf *ectx)
+{
+	struct nfp_elf_elf64_shdr *sec;
+	uint16_t e_shstrndx = rte_le_to_cpu_16(ectx->ehdr->e_shstrndx);
+
+	if (ectx->ehdr->e_shnum <= ectx->ehdr->e_shstrndx) {
+		PMD_DRV_LOG(ERR, "Invalid Index.");
+		return -EINVAL;
+	}
+
+	sec = &ectx->shdrs[e_shstrndx];
+	if (sec == NULL || rte_le_to_cpu_32(sec->sh_type) != NFP_ELF_SHT_STRTAB) {
+		PMD_DRV_LOG(ERR, "Invalid ELF shstrtab.");
+		return -EINVAL;
+	}
+
+	ectx->shstrtab = ectx->shdrs_data[e_shstrndx];
+	ectx->shstrtab_sz = rte_le_to_cpu_64(sec->sh_size);
+
+	return 0;
+}
+
+static int
+nfp_elf_read_first_symtab(struct nfp_elf *ectx)
+{
+	size_t idx;
+	uint32_t sh_type;
+	uint64_t sh_size;
+	struct nfp_elf_elf64_shdr *sec;
+
+	for (idx = 0, sec = ectx->shdrs; idx < ectx->shdrs_cnt; idx++, sec++) {
+		if (sec != NULL) {
+			sh_type = rte_le_to_cpu_32(sec->sh_type);
+			if (sh_type == NFP_ELF_SHT_SYMTAB)
+				break;
+		}
+	}
+
+	sh_size = rte_le_to_cpu_64(sec->sh_size);
+
+	if (idx < ectx->shdrs_cnt && sh_type == NFP_ELF_SHT_SYMTAB) {
+		ectx->shdr_idx_symtab = idx;
+		ectx->syms = ectx->shdrs_data[idx];
+		ectx->syms_cnt = nfp_elf_get_sec_ent_cnt(ectx, idx);
+
+		/* Load symtab's strtab */
+		idx = rte_le_to_cpu_32(sec->sh_link);
+
+		if (idx == NFP_ELF_SHN_UNDEF || idx >= ectx->shdrs_cnt) {
+			PMD_DRV_LOG(ERR, "ELF symtab has no strtab.");
+			return -EINVAL;
+		}
+
+		sec = &ectx->shdrs[idx];
+		sh_type = rte_le_to_cpu_32(sec->sh_type);
+		if (sh_type != NFP_ELF_SHT_STRTAB) {
+			PMD_DRV_LOG(ERR, "ELF symtab has no strtab.");
+			return -EINVAL;
+		}
+
+		if (!nfp_elf_check_sh_size(sh_size)) {
+			PMD_DRV_LOG(ERR, "ELF symtab has invalid strtab.");
+			return -EINVAL;
+		}
+
+		ectx->symstrtab = ectx->shdrs_data[idx];
+		ectx->symstrtab_sz = sh_size;
+	}
+
+	return 0;
+}
+
+static int
+nfp_elf_is_valid_file(uint8_t *buf8)
+{
+	if (buf8[NFP_ELF_EI_MAG0] != NFP_ELF_ELFMAG0 ||
+			buf8[NFP_ELF_EI_MAG1] != NFP_ELF_ELFMAG1 ||
+			buf8[NFP_ELF_EI_MAG2] != NFP_ELF_ELFMAG2 ||
+			buf8[NFP_ELF_EI_MAG3] != NFP_ELF_ELFMAG3 ||
+			buf8[NFP_ELF_EI_VERSION] != NFP_ELF_EV_CURRENT ||
+			buf8[NFP_ELF_EI_DATA] != NFP_ELF_ELFDATA2LSB)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+nfp_elf_is_valid_class(uint8_t *buf8)
+{
+	if (buf8[NFP_ELF_EI_CLASS] != NFP_ELF_ELFCLASS64)
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct nfp_elf *
+nfp_elf_mutable_buf(void *buf,
+		size_t buf_len)
+{
+	int err = 0;
+	uint8_t *buf8 = buf;
+	struct nfp_elf *ectx;
+
+	if (buf == NULL) {
+		PMD_DRV_LOG(ERR, "Invalid parameters.");
+		return NULL;
+	}
+
+	if (buf_len < sizeof(struct nfp_elf_elf64_ehdr)) {
+		PMD_DRV_LOG(ERR, "ELF data too short.");
+		return NULL;
+	}
+
+	ectx = calloc(1, sizeof(struct nfp_elf));
+	if (ectx == NULL) {
+		PMD_DRV_LOG(ERR, "Out of memory.");
+		return NULL;
+	}
+
+	ectx->rev_min = -1;
+	ectx->rev_max = -1;
+	ectx->mip_sh_off = UINT64_MAX;
+
+	err = nfp_elf_is_valid_file(buf8);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Not a valid ELF file.");
+		goto elf_free;
+	}
+
+	err = nfp_elf_is_valid_class(buf8);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Unknown ELF class.");
+		goto elf_free;
+	}
+
+	err = nfp_elf_read_file_headers(ectx, buf);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Failed to read file headers.");
+		goto elf_free;
+	}
+
+	err = nfp_elf_read_section_headers(ectx, buf8, buf_len);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Failed to read section headers.");
+		goto elf_free;
+	}
+
+	err = nfp_elf_read_shstrtab(ectx);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Failed to read shstrtab.");
+		goto elf_free;
+	}
+
+	/* Read first symtab if any, assuming it's the primary or only one */
+	err = nfp_elf_read_first_symtab(ectx);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Failed to read first symtab.");
+		goto elf_free;
+	}
+
+	/* Populate the fw_mip struct if we have a .note for it */
+	if (ectx->mip_shndx != 0) {
+		err = nfp_elf_populate_fw_mip(ectx, buf8);
+		if (err != 0) {
+			PMD_DRV_LOG(ERR, "Failed to populate the fw mip.");
+			goto elf_free;
+		}
+	}
+
+	ectx->_buf = buf;
+	ectx->_bufsz = 0;
+
+	return ectx;
+
+elf_free:
+	nfp_elf_free(ectx);
+
+	return NULL;
+}
+
+int
+nfp_elf_get_fw_version(uint32_t *fw_version,
+		char *fw_name)
+{
+	void *fw_buf;
+	size_t fsize;
+	struct nfp_elf *elf;
+
+	if (rte_firmware_read(fw_name, &fw_buf, &fsize) != 0) {
+		PMD_DRV_LOG(ERR, "firmware %s not found!", fw_name);
+		return -ENOENT;
+	}
+
+	elf = nfp_elf_mutable_buf(fw_buf, fsize);
+	if (elf == NULL) {
+		PMD_DRV_LOG(ERR, "Parse nffw file failed.");
+		free(fw_buf);
+		return -EIO;
+	}
+
+	*fw_version = rte_le_to_cpu_32(elf->fw_mip.fw_version);
+
+	nfp_elf_free(elf);
+	free(fw_buf);
+	return 0;
+}
+
diff --git a/drivers/net/nfp/nfpcore/nfp_elf.h b/drivers/net/nfp/nfpcore/nfp_elf.h
new file mode 100644
index 0000000000..4081af6f01
--- /dev/null
+++ b/drivers/net/nfp/nfpcore/nfp_elf.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Corigine, Inc.
+ * All rights reserved.
+ */
+
+#ifndef __NFP_ELF_H__
+#define __NFP_ELF_H__
+
+#include <stdint.h>
+
+int nfp_elf_get_fw_version(uint32_t *fw_version, char *fw_name);
+
+#endif /* __NFP_ELF_H__ */
diff --git a/drivers/net/nfp/nfpcore/nfp_mip.c b/drivers/net/nfp/nfpcore/nfp_mip.c
index d5ada3687a..98d1d19047 100644
--- a/drivers/net/nfp/nfpcore/nfp_mip.c
+++ b/drivers/net/nfp/nfpcore/nfp_mip.c
@@ -10,30 +10,6 @@
 #include "nfp_logs.h"
 #include "nfp_nffw.h"
 
-#define NFP_MIP_SIGNATURE        rte_cpu_to_le_32(0x0050494d)  /* "MIP\0" */
-#define NFP_MIP_VERSION          rte_cpu_to_le_32(1)
-#define NFP_MIP_MAX_OFFSET       (256 * 1024)
-
-struct nfp_mip {
-	uint32_t signature;
-	uint32_t mip_version;
-	uint32_t mip_size;
-	uint32_t first_entry;
-
-	uint32_t version;
-	uint32_t buildnum;
-	uint32_t buildtime;
-	uint32_t loadtime;
-
-	uint32_t symtab_addr;
-	uint32_t symtab_size;
-	uint32_t strtab_addr;
-	uint32_t strtab_size;
-
-	char name[16];
-	char toolchain[32];
-};
-
 /* Read memory and check if it could be a valid MIP */
 static int
 nfp_mip_try_read(struct nfp_cpp *cpp,
@@ -134,6 +110,12 @@ nfp_mip_name(const struct nfp_mip *mip)
 	return mip->name;
 }
 
+uint32_t
+nfp_mip_fw_version(const struct nfp_mip *mip)
+{
+	return rte_le_to_cpu_32(mip->version);
+}
+
 /**
  * Get the address and size of the MIP symbol table.
  *
diff --git a/drivers/net/nfp/nfpcore/nfp_mip.h b/drivers/net/nfp/nfpcore/nfp_mip.h
index dbd9af31ed..411fe413d7 100644
--- a/drivers/net/nfp/nfpcore/nfp_mip.h
+++ b/drivers/net/nfp/nfpcore/nfp_mip.h
@@ -8,12 +8,80 @@
 
 #include "nfp_cpp.h"
 
-struct nfp_mip;
+/* "MIP\0" */
+#define NFP_MIP_SIGNATURE        rte_cpu_to_le_32(0x0050494d)
+#define NFP_MIP_VERSION          rte_cpu_to_le_32(1)
+
+/* nfp_mip_entry_type */
+#define NFP_MIP_TYPE_FWINFO      0x70000002
+
+/* Each packed struct field is stored as Little Endian */
+struct nfp_mip {
+	rte_le32_t signature;
+	rte_le32_t mip_version;
+
+	rte_le32_t mip_size;
+	rte_le32_t first_entry;
+
+	rte_le32_t version;
+	rte_le32_t buildnum;
+	rte_le32_t buildtime;
+	rte_le32_t loadtime;
+
+	rte_le32_t symtab_addr;
+	rte_le32_t symtab_size;
+	rte_le32_t strtab_addr;
+	rte_le32_t strtab_size;
+
+	char name[16];
+	char toolchain[32];
+};
+
+struct nfp_mip_entry {
+	uint32_t type;
+	uint32_t version;
+	uint32_t offset_next;
+};
+
+/*
+ * A key-value pair has no imposed limit, but it is recommended that
+ * consumers only allocate enough memory for keys they plan to process and
+ * skip over unused keys or ignore values that are longer than expected.
+ *
+ * For MIPv1, this will be preceded by struct nfp_mip_entry.
+ * The entry size will be the size of key_value_strs, round to the next
+ * 4-byte multiple. If entry size is 0, then there are no key-value strings
+ * and it will not contain an empty string.
+ *
+ * The following keys are reserved and possibly set by the linker. The
+ * convention is to start linker-set keys with a capital letter. Reserved
+ * entries will be placed first in key_value_strs, user entries will be
+ * placed next and be sorted alphabetically.
+ * TypeId - Present if a user specified fw_typeid when linking.
+ *
+ * The following keys are reserved, but not used. Their values are in the
+ * root MIP struct.
+ */
+struct nfp_mip_fwinfo_entry {
+	/** The byte size of @p key_value_strs. */
+	uint32_t kv_len;
+
+	/** The number of key-value pairs in the following string. */
+	uint32_t num;
+
+	/**
+	 * A series of NUL terminated strings, terminated by an extra
+	 * NUL which is also the last byte of the entry, so an iterator
+	 * can either check on size or when key[0] == '\0'.
+	 */
+	char key_value_strs[];
+};
 
 struct nfp_mip *nfp_mip_open(struct nfp_cpp *cpp);
 void nfp_mip_close(struct nfp_mip *mip);
 
 const char *nfp_mip_name(const struct nfp_mip *mip);
+uint32_t nfp_mip_fw_version(const struct nfp_mip *mip);
 void nfp_mip_symtab(const struct nfp_mip *mip, uint32_t *addr, uint32_t *size);
 void nfp_mip_strtab(const struct nfp_mip *mip, uint32_t *addr, uint32_t *size);
 
-- 
2.39.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 2/3] net/nfp: reload the firmware only when firmware changed
  2024-02-27 11:15 [PATCH 0/3] reload the firmware as needed Chaoyong He
  2024-02-27 11:15 ` [PATCH 1/3] net/nfp: add the elf module Chaoyong He
@ 2024-02-27 11:15 ` Chaoyong He
  2024-02-27 11:15 ` [PATCH 3/3] net/nfp: add force reload firmware option Chaoyong He
  2024-03-01  8:42 ` [PATCH v2 0/3] reload the firmware as needed Chaoyong He
  3 siblings, 0 replies; 17+ messages in thread
From: Chaoyong He @ 2024-02-27 11:15 UTC (permalink / raw)
  To: dev; +Cc: oss-drivers, Peng Zhang, Chaoyong He, Long Wu

From: Peng Zhang <peng.zhang@corigine.com>

Add the interfaces of getting firmware version from BSP and ELF file,
only reloading the firmware when the version is different, which
means the firmware has changed.

This will accelerate the average startup time for both
multi-PF and single-PF firmware.

Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
Reviewed-by: Long Wu <long.wu@corigine.com>
---
 drivers/net/nfp/nfp_ethdev.c     | 85 +++++++++++++++++++++++++++-----
 drivers/net/nfp/nfp_net_common.c | 17 +++++++
 drivers/net/nfp/nfp_net_common.h |  2 +
 3 files changed, 93 insertions(+), 11 deletions(-)

diff --git a/drivers/net/nfp/nfp_ethdev.c b/drivers/net/nfp/nfp_ethdev.c
index 3ece806487..902a62a86b 100644
--- a/drivers/net/nfp/nfp_ethdev.c
+++ b/drivers/net/nfp/nfp_ethdev.c
@@ -14,6 +14,7 @@
 #include "nfd3/nfp_nfd3.h"
 #include "nfdk/nfp_nfdk.h"
 #include "nfpcore/nfp_cpp.h"
+#include "nfpcore/nfp_elf.h"
 #include "nfpcore/nfp_hwinfo.h"
 #include "nfpcore/nfp_rtsym.h"
 #include "nfpcore/nfp_nsp.h"
@@ -1070,6 +1071,35 @@ nfp_fw_unload(struct nfp_cpp *cpp)
 	nfp_nsp_close(nsp);
 }
 
+static int
+nfp_fw_check_change(struct nfp_cpp *cpp,
+		char *fw_name,
+		bool *fw_changed)
+{
+	int ret;
+	struct nfp_net_hw hw;
+	uint32_t new_version = 0;
+	uint32_t old_version = 0;
+
+	ret = nfp_elf_get_fw_version(&new_version, fw_name);
+	if (ret != 0)
+		return ret;
+
+	hw.cpp = cpp;
+	nfp_net_get_fw_version(&hw, &old_version);
+
+	if (new_version != old_version) {
+		PMD_DRV_LOG(INFO, "FW version is changed, new %u, old %u",
+				new_version, old_version);
+		*fw_changed = true;
+	} else {
+		PMD_DRV_LOG(INFO, "FW version is not changed and is %u", new_version);
+		*fw_changed = false;
+	}
+
+	return 0;
+}
+
 static int
 nfp_fw_reload(struct nfp_nsp *nsp,
 		char *fw_name)
@@ -1135,15 +1165,39 @@ nfp_fw_skip_load(const struct nfp_dev_info *dev_info,
 
 	return false;
 }
+static int
+nfp_fw_reload_for_single_pf(struct nfp_nsp *nsp,
+		char *fw_name,
+		struct nfp_cpp *cpp)
+{
+	int ret;
+	bool fw_changed = true;
+
+	if (nfp_nsp_fw_loaded(nsp)) {
+		ret = nfp_fw_check_change(cpp, fw_name, &fw_changed);
+		if (ret != 0)
+			return ret;
+	}
+
+	if (!fw_changed)
+		return 0;
+
+	ret = nfp_fw_reload(nsp, fw_name);
+	if (ret != 0)
+		return ret;
+
+	return 0;
+}
 
 static int
-nfp_fw_reload_for_multipf(struct nfp_nsp *nsp,
+nfp_fw_reload_for_multi_pf(struct nfp_nsp *nsp,
 		char *fw_name,
 		struct nfp_cpp *cpp,
 		const struct nfp_dev_info *dev_info,
 		struct nfp_multi_pf *multi_pf)
 {
 	int err;
+	bool fw_changed = true;
 	bool skip_load_fw = false;
 
 	err = nfp_net_keepalive_init(cpp, multi_pf);
@@ -1154,27 +1208,36 @@ nfp_fw_reload_for_multipf(struct nfp_nsp *nsp,
 
 	err = nfp_net_keepalive_start(multi_pf);
 	if (err != 0) {
-		nfp_net_keepalive_uninit(multi_pf);
 		PMD_DRV_LOG(ERR, "NFP write beat failed");
-		return err;
+		goto keepalive_uninit;
+	}
+
+	if (nfp_nsp_fw_loaded(nsp)) {
+		err = nfp_fw_check_change(cpp, fw_name, &fw_changed);
+		if (err != 0)
+			goto keepalive_stop;
 	}
 
-	if (nfp_nsp_fw_loaded(nsp))
+	if (!fw_changed)
 		skip_load_fw = nfp_fw_skip_load(dev_info, multi_pf);
 
 	if (skip_load_fw)
 		return 0;
 
 	err = nfp_fw_reload(nsp, fw_name);
-	if (err != 0) {
-		nfp_net_keepalive_stop(multi_pf);
-		nfp_net_keepalive_uninit(multi_pf);
-		return err;
-	}
+	if (err != 0)
+		goto keepalive_stop;
 
 	nfp_net_keepalive_clear_others(dev_info, multi_pf);
 
 	return 0;
+
+keepalive_stop:
+	nfp_net_keepalive_stop(multi_pf);
+keepalive_uninit:
+	nfp_net_keepalive_uninit(multi_pf);
+
+	return err;
 }
 
 static int
@@ -1231,9 +1294,9 @@ nfp_fw_setup(struct rte_pci_device *dev,
 	}
 
 	if (multi_pf->enabled)
-		err = nfp_fw_reload_for_multipf(nsp, fw_name, cpp, dev_info, multi_pf);
+		err = nfp_fw_reload_for_multi_pf(nsp, fw_name, cpp, dev_info, multi_pf);
 	else
-		err = nfp_fw_reload(nsp, fw_name);
+		err = nfp_fw_reload_for_single_pf(nsp, fw_name, cpp);
 
 	nfp_nsp_close(nsp);
 	return err;
diff --git a/drivers/net/nfp/nfp_net_common.c b/drivers/net/nfp/nfp_net_common.c
index 26ea4ec92f..20e628bfd1 100644
--- a/drivers/net/nfp/nfp_net_common.c
+++ b/drivers/net/nfp/nfp_net_common.c
@@ -2090,6 +2090,23 @@ nfp_net_get_nsp_info(struct nfp_net_hw *hw,
 	nfp_nsp_close(nsp);
 }
 
+void
+nfp_net_get_fw_version(struct nfp_net_hw *hw,
+		uint32_t *mip_version)
+{
+	struct nfp_mip *mip;
+
+	mip = nfp_mip_open(hw->cpp);
+	if (mip == NULL) {
+		*mip_version = 0;
+		return;
+	}
+
+	*mip_version = nfp_mip_fw_version(mip);
+
+	nfp_mip_close(mip);
+}
+
 static void
 nfp_net_get_mip_name(struct nfp_net_hw *hw,
 		char *mip_name)
diff --git a/drivers/net/nfp/nfp_net_common.h b/drivers/net/nfp/nfp_net_common.h
index 1c24045b7d..9c46bfd4f6 100644
--- a/drivers/net/nfp/nfp_net_common.h
+++ b/drivers/net/nfp/nfp_net_common.h
@@ -316,6 +316,8 @@ int nfp_net_fec_get(struct rte_eth_dev *dev,
 		uint32_t *fec_capa);
 int nfp_net_fec_set(struct rte_eth_dev *dev,
 		uint32_t fec_capa);
+void nfp_net_get_fw_version(struct nfp_net_hw *hw,
+		uint32_t *fw_version);
 
 #define NFP_PRIV_TO_APP_FW_NIC(app_fw_priv)\
 	((struct nfp_app_fw_nic *)app_fw_priv)
-- 
2.39.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 3/3] net/nfp: add force reload firmware option
  2024-02-27 11:15 [PATCH 0/3] reload the firmware as needed Chaoyong He
  2024-02-27 11:15 ` [PATCH 1/3] net/nfp: add the elf module Chaoyong He
  2024-02-27 11:15 ` [PATCH 2/3] net/nfp: reload the firmware only when firmware changed Chaoyong He
@ 2024-02-27 11:15 ` Chaoyong He
  2024-02-28 16:53   ` Ferruh Yigit
  2024-03-01  8:42 ` [PATCH v2 0/3] reload the firmware as needed Chaoyong He
  3 siblings, 1 reply; 17+ messages in thread
From: Chaoyong He @ 2024-02-27 11:15 UTC (permalink / raw)
  To: dev; +Cc: oss-drivers, Peng Zhang, Chaoyong He

From: Peng Zhang <peng.zhang@corigine.com>

Add an option to force reload the firmware.

This option can be ignored in some case, for example:
When using a 2 port NFP card and both with this reload firmware option,
only the first one will cause the firmware reload and the second one
will be ignored.

Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
---
 drivers/net/nfp/nfp_ethdev.c     | 104 +++++++++++++++++++++++++++----
 drivers/net/nfp/nfp_net_common.h |   8 +++
 2 files changed, 100 insertions(+), 12 deletions(-)

diff --git a/drivers/net/nfp/nfp_ethdev.c b/drivers/net/nfp/nfp_ethdev.c
index 902a62a86b..8c38b4e134 100644
--- a/drivers/net/nfp/nfp_ethdev.c
+++ b/drivers/net/nfp/nfp_ethdev.c
@@ -9,6 +9,7 @@
 
 #include <eal_firmware.h>
 #include <rte_alarm.h>
+#include <rte_kvargs.h>
 
 #include "flower/nfp_flower.h"
 #include "nfd3/nfp_nfd3.h"
@@ -31,6 +32,71 @@
 #define NFP_NET_APP_CAP_SP_INDIFF       RTE_BIT64(0) /* Indifferent to port speed */
 
 #define NFP_PF_DRIVER_NAME net_nfp_pf
+#define NFP_PF_FORCE_RELOAD_FW   "force_reload_fw"
+
+static int
+nfp_devarg_handle_int(const char *key,
+		const char *value,
+		void *extra_args)
+{
+	char *end_ptr;
+	uint64_t *num = extra_args;
+
+	if (value == NULL)
+		return -EPERM;
+
+	*num = strtoul(value, &end_ptr, 10);
+	if (*num == ULONG_MAX) {
+		PMD_DRV_LOG(ERR, "%s: '%s' is not a valid param", key, value);
+		return -ERANGE;
+	} else if (value == end_ptr) {
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+static void
+nfp_devarg_parse_force_reload_fw(struct rte_kvargs *kvlist,
+		bool *force_reload_fw)
+{
+	int ret;
+	uint64_t value;
+
+
+	if (rte_kvargs_count(kvlist, NFP_PF_FORCE_RELOAD_FW) != 1)
+		return;
+
+	ret = rte_kvargs_process(kvlist, NFP_PF_FORCE_RELOAD_FW, &nfp_devarg_handle_int, &value);
+	if (ret != 0)
+		return;
+
+	if (value == 1)
+		*force_reload_fw = true;
+	else if (value == 0)
+		*force_reload_fw = false;
+	else
+		PMD_DRV_LOG(ERR, "The param does not work, the format is %s=0/1",
+				NFP_PF_FORCE_RELOAD_FW);
+}
+
+static void
+nfp_devargs_parse(struct nfp_devargs *nfp_devargs_param,
+		const struct rte_devargs *devargs)
+{
+	struct rte_kvargs *kvlist;
+
+	if (devargs == NULL)
+		return;
+
+	kvlist = rte_kvargs_parse(devargs->args, NULL);
+	if (kvlist == NULL)
+		return;
+
+	nfp_devarg_parse_force_reload_fw(kvlist, &nfp_devargs_param->force_reload_fw);
+
+	rte_kvargs_free(kvlist);
+}
 
 static void
 nfp_net_pf_read_mac(struct nfp_app_fw_nic *app_fw_nic,
@@ -1116,7 +1182,8 @@ nfp_fw_reload(struct nfp_nsp *nsp,
 
 static bool
 nfp_fw_skip_load(const struct nfp_dev_info *dev_info,
-		struct nfp_multi_pf *multi_pf)
+		struct nfp_multi_pf *multi_pf,
+		bool *reload_fw)
 {
 	uint8_t i;
 	uint64_t tmp_beat;
@@ -1150,6 +1217,11 @@ nfp_fw_skip_load(const struct nfp_dev_info *dev_info,
 				in_use++;
 				abnormal--;
 				beat[port_num] = 0;
+				if (*reload_fw) {
+					*reload_fw = false;
+					PMD_DRV_LOG(ERR, "The param %s does not work",
+							NFP_PF_FORCE_RELOAD_FW);
+				}
 			}
 		}
 
@@ -1168,12 +1240,13 @@ nfp_fw_skip_load(const struct nfp_dev_info *dev_info,
 static int
 nfp_fw_reload_for_single_pf(struct nfp_nsp *nsp,
 		char *fw_name,
-		struct nfp_cpp *cpp)
+		struct nfp_cpp *cpp,
+		bool force_reload_fw)
 {
 	int ret;
 	bool fw_changed = true;
 
-	if (nfp_nsp_fw_loaded(nsp)) {
+	if (nfp_nsp_fw_loaded(nsp) && !force_reload_fw) {
 		ret = nfp_fw_check_change(cpp, fw_name, &fw_changed);
 		if (ret != 0)
 			return ret;
@@ -1194,11 +1267,13 @@ nfp_fw_reload_for_multi_pf(struct nfp_nsp *nsp,
 		char *fw_name,
 		struct nfp_cpp *cpp,
 		const struct nfp_dev_info *dev_info,
-		struct nfp_multi_pf *multi_pf)
+		struct nfp_multi_pf *multi_pf,
+		bool force_reload_fw)
 {
 	int err;
 	bool fw_changed = true;
 	bool skip_load_fw = false;
+	bool reload_fw = force_reload_fw;
 
 	err = nfp_net_keepalive_init(cpp, multi_pf);
 	if (err != 0) {
@@ -1212,16 +1287,16 @@ nfp_fw_reload_for_multi_pf(struct nfp_nsp *nsp,
 		goto keepalive_uninit;
 	}
 
-	if (nfp_nsp_fw_loaded(nsp)) {
+	if (nfp_nsp_fw_loaded(nsp) && !reload_fw) {
 		err = nfp_fw_check_change(cpp, fw_name, &fw_changed);
 		if (err != 0)
 			goto keepalive_stop;
 	}
 
-	if (!fw_changed)
-		skip_load_fw = nfp_fw_skip_load(dev_info, multi_pf);
+	if (!fw_changed || reload_fw)
+		skip_load_fw = nfp_fw_skip_load(dev_info, multi_pf, &reload_fw);
 
-	if (skip_load_fw)
+	if (skip_load_fw && !reload_fw)
 		return 0;
 
 	err = nfp_fw_reload(nsp, fw_name);
@@ -1246,7 +1321,8 @@ nfp_fw_setup(struct rte_pci_device *dev,
 		struct nfp_eth_table *nfp_eth_table,
 		struct nfp_hwinfo *hwinfo,
 		const struct nfp_dev_info *dev_info,
-		struct nfp_multi_pf *multi_pf)
+		struct nfp_multi_pf *multi_pf,
+		bool force_reload_fw)
 {
 	int err;
 	char fw_name[125];
@@ -1294,9 +1370,10 @@ nfp_fw_setup(struct rte_pci_device *dev,
 	}
 
 	if (multi_pf->enabled)
-		err = nfp_fw_reload_for_multi_pf(nsp, fw_name, cpp, dev_info, multi_pf);
+		err = nfp_fw_reload_for_multi_pf(nsp, fw_name, cpp, dev_info, multi_pf,
+				force_reload_fw);
 	else
-		err = nfp_fw_reload_for_single_pf(nsp, fw_name, cpp);
+		err = nfp_fw_reload_for_single_pf(nsp, fw_name, cpp, force_reload_fw);
 
 	nfp_nsp_close(nsp);
 	return err;
@@ -1769,8 +1846,10 @@ nfp_pf_init(struct rte_pci_device *pci_dev)
 		nfp_eth_set_configured(cpp, index, 0);
 	}
 
+	nfp_devargs_parse(&pf_dev->devargs, pci_dev->device.devargs);
+
 	if (nfp_fw_setup(pci_dev, cpp, nfp_eth_table, hwinfo,
-			dev_info, &pf_dev->multi_pf) != 0) {
+			dev_info, &pf_dev->multi_pf, pf_dev->devargs.force_reload_fw) != 0) {
 		PMD_INIT_LOG(ERR, "Error when uploading firmware");
 		ret = -EIO;
 		goto eth_table_cleanup;
@@ -2151,3 +2230,4 @@ static struct rte_pci_driver rte_nfp_net_pf_pmd = {
 RTE_PMD_REGISTER_PCI(NFP_PF_DRIVER_NAME, rte_nfp_net_pf_pmd);
 RTE_PMD_REGISTER_PCI_TABLE(NFP_PF_DRIVER_NAME, pci_id_nfp_pf_net_map);
 RTE_PMD_REGISTER_KMOD_DEP(NFP_PF_DRIVER_NAME, "* igb_uio | uio_pci_generic | vfio");
+RTE_PMD_REGISTER_PARAM_STRING(NFP_PF_DRIVER_NAME, NFP_PF_FORCE_RELOAD_FW "=<0|1>");
diff --git a/drivers/net/nfp/nfp_net_common.h b/drivers/net/nfp/nfp_net_common.h
index 9c46bfd4f6..628c0d3491 100644
--- a/drivers/net/nfp/nfp_net_common.h
+++ b/drivers/net/nfp/nfp_net_common.h
@@ -94,6 +94,11 @@ struct nfp_process_share {
 	struct nfp_flower_service *fl_service;
 };
 
+struct nfp_devargs {
+	/** Force reload firmware */
+	bool force_reload_fw;
+};
+
 struct nfp_pf_dev {
 	/** Backpointer to associated pci device */
 	struct rte_pci_device *pci_dev;
@@ -129,6 +134,9 @@ struct nfp_pf_dev {
 	/** Synchronized info */
 	struct nfp_sync *sync;
 	struct nfp_process_share process_share;
+
+	/** NFP devarg param */
+	struct nfp_devargs devargs;
 };
 
 #define NFP_NET_FLOW_LIMIT    1024
-- 
2.39.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/3] net/nfp: add the elf module
  2024-02-27 11:15 ` [PATCH 1/3] net/nfp: add the elf module Chaoyong He
@ 2024-02-28 16:50   ` Ferruh Yigit
  2024-03-01  2:38     ` Chaoyong He
  2024-02-28 22:18   ` Stephen Hemminger
  1 sibling, 1 reply; 17+ messages in thread
From: Ferruh Yigit @ 2024-02-28 16:50 UTC (permalink / raw)
  To: Chaoyong He, dev; +Cc: oss-drivers, Peng Zhang, Long Wu

On 2/27/2024 11:15 AM, Chaoyong He wrote:
> From: Peng Zhang <peng.zhang@corigine.com>
> 
> Add the elf module, which can get mip information from the
> firmware ELF file.
>

What is MIP?
Is it in the .note section of the ELF binary?
How it is used?

Source code always refers it as MIP, can you please clarify it in the
commit log?


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 3/3] net/nfp: add force reload firmware option
  2024-02-27 11:15 ` [PATCH 3/3] net/nfp: add force reload firmware option Chaoyong He
@ 2024-02-28 16:53   ` Ferruh Yigit
  2024-03-01  2:06     ` Chaoyong He
  0 siblings, 1 reply; 17+ messages in thread
From: Ferruh Yigit @ 2024-02-28 16:53 UTC (permalink / raw)
  To: Chaoyong He, dev; +Cc: oss-drivers, Peng Zhang

On 2/27/2024 11:15 AM, Chaoyong He wrote:
> From: Peng Zhang <peng.zhang@corigine.com>
> 
> Add an option to force reload the firmware.
> 
> This option can be ignored in some case, for example:
> When using a 2 port NFP card and both with this reload firmware option,
> only the first one will cause the firmware reload and the second one
> will be ignored.
> 
> Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
> Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
> ---
>  drivers/net/nfp/nfp_ethdev.c     | 104 +++++++++++++++++++++++++++----
>  drivers/net/nfp/nfp_net_common.h |   8 +++
>  2 files changed, 100 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/net/nfp/nfp_ethdev.c b/drivers/net/nfp/nfp_ethdev.c
> index 902a62a86b..8c38b4e134 100644
> --- a/drivers/net/nfp/nfp_ethdev.c
> +++ b/drivers/net/nfp/nfp_ethdev.c
> @@ -9,6 +9,7 @@
>  
>  #include <eal_firmware.h>
>  #include <rte_alarm.h>
> +#include <rte_kvargs.h>
>  
>  #include "flower/nfp_flower.h"
>  #include "nfd3/nfp_nfd3.h"
> @@ -31,6 +32,71 @@
>  #define NFP_NET_APP_CAP_SP_INDIFF       RTE_BIT64(0) /* Indifferent to port speed */
>  
>  #define NFP_PF_DRIVER_NAME net_nfp_pf
> +#define NFP_PF_FORCE_RELOAD_FW   "force_reload_fw"
> +
>

Can you please document the new devarg in the driver documentation,
'doc/guides/nics/nfp.rst'?

Also you may want to document FW upgrade process, as it checks FW
version by default and this can be overridden by devarg etc..

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/3] net/nfp: add the elf module
  2024-02-27 11:15 ` [PATCH 1/3] net/nfp: add the elf module Chaoyong He
  2024-02-28 16:50   ` Ferruh Yigit
@ 2024-02-28 22:18   ` Stephen Hemminger
  2024-03-01 17:22     ` Ferruh Yigit
  1 sibling, 1 reply; 17+ messages in thread
From: Stephen Hemminger @ 2024-02-28 22:18 UTC (permalink / raw)
  To: Chaoyong He; +Cc: dev, oss-drivers, Peng Zhang, Long Wu

On Tue, 27 Feb 2024 19:15:49 +0800
Chaoyong He <chaoyong.he@corigine.com> wrote:

> From: Peng Zhang <peng.zhang@corigine.com>
> 
> Add the elf module, which can get mip information from the
> firmware ELF file.
> 
> Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
> Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
> Reviewed-by: Long Wu <long.wu@corigine.com>
> ---

Why are you rolling your own ELF parser?
There are libraries to do this such as libelf.
Libelf is already used in the BPF part of DPDK.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH 3/3] net/nfp: add force reload firmware option
  2024-02-28 16:53   ` Ferruh Yigit
@ 2024-03-01  2:06     ` Chaoyong He
  0 siblings, 0 replies; 17+ messages in thread
From: Chaoyong He @ 2024-03-01  2:06 UTC (permalink / raw)
  To: Ferruh Yigit, dev; +Cc: oss-drivers, Nole Zhang

> On 2/27/2024 11:15 AM, Chaoyong He wrote:
> > From: Peng Zhang <peng.zhang@corigine.com>
> >
> > Add an option to force reload the firmware.
> >
> > This option can be ignored in some case, for example:
> > When using a 2 port NFP card and both with this reload firmware
> > option, only the first one will cause the firmware reload and the
> > second one will be ignored.
> >
> > Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
> > Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
> > ---
> >  drivers/net/nfp/nfp_ethdev.c     | 104 +++++++++++++++++++++++++++--
> --
> >  drivers/net/nfp/nfp_net_common.h |   8 +++
> >  2 files changed, 100 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/net/nfp/nfp_ethdev.c
> > b/drivers/net/nfp/nfp_ethdev.c index 902a62a86b..8c38b4e134 100644
> > --- a/drivers/net/nfp/nfp_ethdev.c
> > +++ b/drivers/net/nfp/nfp_ethdev.c
> > @@ -9,6 +9,7 @@
> >
> >  #include <eal_firmware.h>
> >  #include <rte_alarm.h>
> > +#include <rte_kvargs.h>
> >
> >  #include "flower/nfp_flower.h"
> >  #include "nfd3/nfp_nfd3.h"
> > @@ -31,6 +32,71 @@
> >  #define NFP_NET_APP_CAP_SP_INDIFF       RTE_BIT64(0) /* Indifferent to
> port speed */
> >
> >  #define NFP_PF_DRIVER_NAME net_nfp_pf
> > +#define NFP_PF_FORCE_RELOAD_FW   "force_reload_fw"
> > +
> >
> 
> Can you please document the new devarg in the driver documentation,
> 'doc/guides/nics/nfp.rst'?
> 
> Also you may want to document FW upgrade process, as it checks FW version
> by default and this can be overridden by devarg etc..

Okay, we will document it in the next version patch series, thanks.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH 1/3] net/nfp: add the elf module
  2024-02-28 16:50   ` Ferruh Yigit
@ 2024-03-01  2:38     ` Chaoyong He
  0 siblings, 0 replies; 17+ messages in thread
From: Chaoyong He @ 2024-03-01  2:38 UTC (permalink / raw)
  To: Ferruh Yigit, dev; +Cc: oss-drivers, Nole Zhang, Long Wu

> On 2/27/2024 11:15 AM, Chaoyong He wrote:
> > From: Peng Zhang <peng.zhang@corigine.com>
> >
> > Add the elf module, which can get mip information from the firmware
> > ELF file.
> >
> 
> What is MIP?

Microcode Information Page 

> Is it in the .note section of the ELF binary?

Yes

> How it is used?

Which is an interface driver can get some information from firmware.
We have a 'nfp_mip' module to manage the MIP of loaded firmware, and we add 'nfp_elf' module to directly parse the firmware ELF file.
Then we compare the 'version' filed of MIP.

> 
> Source code always refers it as MIP, can you please clarify it in the commit log?

Okay


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 0/3] reload the firmware as needed
  2024-02-27 11:15 [PATCH 0/3] reload the firmware as needed Chaoyong He
                   ` (2 preceding siblings ...)
  2024-02-27 11:15 ` [PATCH 3/3] net/nfp: add force reload firmware option Chaoyong He
@ 2024-03-01  8:42 ` Chaoyong He
  2024-03-01  8:42   ` [PATCH v2 1/3] net/nfp: add the elf module Chaoyong He
                     ` (3 more replies)
  3 siblings, 4 replies; 17+ messages in thread
From: Chaoyong He @ 2024-03-01  8:42 UTC (permalink / raw)
  To: dev; +Cc: oss-drivers, Chaoyong He

Add the necessary logic to get firmware version from firmware file, and
only reload the firmware when the firmware version changed.

Also add a device argument which can force reload the firmware and
ignore the firmware version.

---
v2:
* Update commit log to explain what 'MIP' is.
* Document about the new add 'force_reload_fw' device argument.
---

Peng Zhang (3):
  net/nfp: add the elf module
  net/nfp: reload the firmware only when firmware changed
  net/nfp: add force reload firmware option

 doc/guides/nics/nfp.rst           |   21 +
 drivers/net/nfp/meson.build       |    1 +
 drivers/net/nfp/nfp_ethdev.c      |  177 ++++-
 drivers/net/nfp/nfp_net_common.c  |   17 +
 drivers/net/nfp/nfp_net_common.h  |   10 +
 drivers/net/nfp/nfpcore/nfp_elf.c | 1079 +++++++++++++++++++++++++++++
 drivers/net/nfp/nfpcore/nfp_elf.h |   13 +
 drivers/net/nfp/nfpcore/nfp_mip.c |   30 +-
 drivers/net/nfp/nfpcore/nfp_mip.h |   70 +-
 9 files changed, 1376 insertions(+), 42 deletions(-)
 create mode 100644 drivers/net/nfp/nfpcore/nfp_elf.c
 create mode 100644 drivers/net/nfp/nfpcore/nfp_elf.h

-- 
2.39.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 1/3] net/nfp: add the elf module
  2024-03-01  8:42 ` [PATCH v2 0/3] reload the firmware as needed Chaoyong He
@ 2024-03-01  8:42   ` Chaoyong He
  2024-03-01  8:42   ` [PATCH v2 2/3] net/nfp: reload the firmware only when firmware changed Chaoyong He
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 17+ messages in thread
From: Chaoyong He @ 2024-03-01  8:42 UTC (permalink / raw)
  To: dev; +Cc: oss-drivers, Peng Zhang, Chaoyong He, Long Wu

From: Peng Zhang <peng.zhang@corigine.com>

Add the 'nfp_elf' module, which can get new MIP(Microcode Information Page)
information from the firmware ELF file.

Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
Reviewed-by: Long Wu <long.wu@corigine.com>
---
 drivers/net/nfp/meson.build       |    1 +
 drivers/net/nfp/nfpcore/nfp_elf.c | 1079 +++++++++++++++++++++++++++++
 drivers/net/nfp/nfpcore/nfp_elf.h |   13 +
 drivers/net/nfp/nfpcore/nfp_mip.c |   30 +-
 drivers/net/nfp/nfpcore/nfp_mip.h |   70 +-
 5 files changed, 1168 insertions(+), 25 deletions(-)
 create mode 100644 drivers/net/nfp/nfpcore/nfp_elf.c
 create mode 100644 drivers/net/nfp/nfpcore/nfp_elf.h

diff --git a/drivers/net/nfp/meson.build b/drivers/net/nfp/meson.build
index e376fd328f..959ca01844 100644
--- a/drivers/net/nfp/meson.build
+++ b/drivers/net/nfp/meson.build
@@ -18,6 +18,7 @@ sources = files(
         'nfdk/nfp_nfdk_dp.c',
         'nfpcore/nfp_cppcore.c',
         'nfpcore/nfp_crc.c',
+        'nfpcore/nfp_elf.c',
         'nfpcore/nfp_hwinfo.c',
         'nfpcore/nfp_mip.c',
         'nfpcore/nfp_mutex.c',
diff --git a/drivers/net/nfp/nfpcore/nfp_elf.c b/drivers/net/nfp/nfpcore/nfp_elf.c
new file mode 100644
index 0000000000..fbd350589b
--- /dev/null
+++ b/drivers/net/nfp/nfpcore/nfp_elf.c
@@ -0,0 +1,1079 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Corigine, Inc.
+ * All rights reserved.
+ */
+
+#include "nfp_elf.h"
+
+#include <malloc.h>
+#include <stdbool.h>
+#include <ethdev_pci.h>
+
+#include <nfp_platform.h>
+#include <rte_common.h>
+#include <eal_firmware.h>
+
+#include "nfp_logs.h"
+#include "nfp_mip.h"
+
+/*
+ * NFP Chip Families.
+ *
+ * These are not enums, because they need to be microcode compatible.
+ * They are also not maskable.
+ *
+ * Note: The NFP-4xxx family is handled as NFP-6xxx in most software
+ * components.
+ */
+#define NFP_CHIP_FAMILY_NFP3800 0x3800
+#define NFP_CHIP_FAMILY_NFP6000 0x6000
+
+/* Standard ELF */
+#define NFP_ELF_EI_NIDENT     16
+#define NFP_ELF_EI_MAG0       0
+#define NFP_ELF_EI_MAG1       1
+#define NFP_ELF_EI_MAG2       2
+#define NFP_ELF_EI_MAG3       3
+#define NFP_ELF_EI_CLASS      4
+#define NFP_ELF_EI_DATA       5
+#define NFP_ELF_EI_VERSION    6
+#define NFP_ELF_EI_PAD        7
+#define NFP_ELF_ELFMAG0       0x7f
+#define NFP_ELF_ELFMAG1       'E'
+#define NFP_ELF_ELFMAG2       'L'
+#define NFP_ELF_ELFMAG3       'F'
+#define NFP_ELF_ELFCLASSNONE  0
+#define NFP_ELF_ELFCLASS32    1
+#define NFP_ELF_ELFCLASS64    2
+#define NFP_ELF_ELFDATANONE   0
+#define NFP_ELF_ELFDATA2LSB   1
+#define NFP_ELF_ELFDATA2MSB   2
+
+#define NFP_ELF_ET_NONE       0
+#define NFP_ELF_ET_REL        1
+#define NFP_ELF_ET_EXEC       2
+#define NFP_ELF_ET_DYN        3
+#define NFP_ELF_ET_CORE       4
+#define NFP_ELF_ET_LOPROC     0xFF00
+#define NFP_ELF_ET_HIPROC     0xFFFF
+#define NFP_ELF_ET_NFP_PARTIAL_REL   (NFP_ELF_ET_LOPROC + NFP_ELF_ET_REL)
+#define NFP_ELF_ET_NFP_PARTIAL_EXEC  (NFP_ELF_ET_LOPROC + NFP_ELF_ET_EXEC)
+
+#define NFP_ELF_EM_NFP        250
+#define NFP_ELF_EM_NFP6000    0x6000
+
+#define NFP_ELF_SHT_NULL      0
+#define NFP_ELF_SHT_PROGBITS  1
+#define NFP_ELF_SHT_SYMTAB    2
+#define NFP_ELF_SHT_STRTAB    3
+#define NFP_ELF_SHT_RELA      4
+#define NFP_ELF_SHT_HASH      5
+#define NFP_ELF_SHT_DYNAMIC   6
+#define NFP_ELF_SHT_NOTE      7
+#define NFP_ELF_SHT_NOBITS    8
+#define NFP_ELF_SHT_REL       9
+#define NFP_ELF_SHT_SHLIB     10
+#define NFP_ELF_SHT_DYNSYM    11
+#define NFP_ELF_SHT_LOPROC    0x70000000
+#define NFP_ELF_SHT_HIPROC    0x7fffffff
+#define NFP_ELF_SHT_LOUSER    0x80000000
+#define NFP_ELF_SHT_HIUSER    0x8fffffff
+
+#define NFP_ELF_EV_NONE       0
+#define NFP_ELF_EV_CURRENT    1
+
+#define NFP_ELF_SHN_UNDEF     0
+
+/* EM_NFP ELF flags */
+
+/*
+ * Valid values for FAMILY are:
+ * 0x6000 - NFP-6xxx/NFP-4xxx
+ * 0x3800 - NFP-38xx
+ */
+#define NFP_ELF_EF_NFP_FAMILY_MASK        0xFFFF
+#define NFP_ELF_EF_NFP_FAMILY_LSB         8
+
+#define NFP_ELF_SHT_NFP_MECONFIG          (NFP_ELF_SHT_LOPROC + 1)
+#define NFP_ELF_SHT_NFP_INITREG           (NFP_ELF_SHT_LOPROC + 2)
+#define NFP_ELF_SHT_UOF_DEBUG             (NFP_ELF_SHT_LOUSER)
+
+/* NFP target revision note type */
+#define NFP_ELT_NOTE_NAME_NFP             "NFP\0"
+#define NFP_ELT_NOTE_NAME_NFP_SZ          4
+#define NFP_ELT_NOTE_NAME_NFP_USER        "NFP_USR\0"
+#define NFP_ELT_NOTE_NAME_NFP_USER_SZ     8
+#define NFP_ELF_NT_NFP_BUILD_INFO         0x100
+#define NFP_ELF_NT_NFP_REVS               0x101
+#define NFP_ELF_NT_NFP_MIP_LOCATION       0x102
+#define NFP_ELF_NT_NFP_USER               0xf0000000
+
+
+/* Standard ELF structures */
+struct nfp_elf_elf64_ehdr {
+	uint8_t e_ident[NFP_ELF_EI_NIDENT];
+	rte_le16_t e_type;
+	rte_le16_t e_machine;
+	rte_le32_t e_version;
+	rte_le64_t e_entry;
+	rte_le64_t e_phoff;
+	rte_le64_t e_shoff;
+	rte_le32_t e_flags;
+	rte_le16_t e_ehsize;
+	rte_le16_t e_phentsize;
+	rte_le16_t e_phnum;
+	rte_le16_t e_shentsize;
+	rte_le16_t e_shnum;
+	rte_le16_t e_shstrndx;
+};
+
+struct nfp_elf_elf64_shdr {
+	rte_le32_t sh_name;
+	rte_le32_t sh_type;
+	rte_le64_t sh_flags;
+	rte_le64_t sh_addr;
+	rte_le64_t sh_offset;
+	rte_le64_t sh_size;
+	rte_le32_t sh_link;
+	rte_le32_t sh_info;
+	rte_le64_t sh_addralign;
+	rte_le64_t sh_entsize;
+};
+
+struct nfp_elf_elf64_sym {
+	rte_le32_t st_name;
+	uint8_t st_info;
+	uint8_t st_other;
+	rte_le16_t st_shndx;
+	rte_le64_t st_value;
+	rte_le64_t st_size;
+};
+
+struct nfp_elf_elf64_rel {
+	rte_le64_t r_offset;
+	rte_le64_t r_info;
+};
+
+struct nfp_elf_elf64_nhdr {
+	rte_le32_t n_namesz;
+	rte_le32_t n_descsz;
+	rte_le32_t n_type;
+};
+
+/* NFP specific structures */
+struct nfp_elf_elf_meconfig {
+	rte_le32_t ctx_enables;
+	rte_le32_t entry;
+	rte_le32_t misc_control;
+	rte_le32_t reserved;
+};
+
+struct nfp_elf_elf_initregentry {
+	rte_le32_t w0;
+	rte_le32_t cpp_offset_lo;
+	rte_le32_t val;
+	rte_le32_t mask;
+};
+
+/* NFP NFFW ELF struct and API */
+struct nfp_elf_user_note {
+	const char *name;
+	uint32_t data_sz;
+	void *data;
+};
+
+/*
+ * nfp_elf_fw_mip contains firmware related fields from the MIP as well as the
+ * MIP location in the NFFW file. All fields are only valid if shndx > 0.
+ *
+ * This struct will only be available if the firmware contains a .note section
+ * with a note of type NFP_ELF_NT_NFP_MIP_LOCATION.
+ */
+struct nfp_elf_fw_mip {
+	size_t shndx;
+	uint64_t sh_offset;
+	rte_le32_t mip_ver;      /**< Version of the format of the MIP itself */
+
+	rte_le32_t fw_version;
+	rte_le32_t fw_buildnum;
+	rte_le32_t fw_buildtime;
+	char fw_name[20];        /**< At most 16 chars, 17 ensures '\0', round up */
+	const char *fw_typeid;   /**< NULL if none set */
+};
+
+/*
+ * It is preferred to access this struct via the nfp_elf functions
+ * rather than directly.
+ */
+struct nfp_elf {
+	struct nfp_elf_elf64_ehdr *ehdr;
+	struct nfp_elf_elf64_shdr *shdrs;
+	size_t shdrs_cnt;
+	void **shdrs_data;
+
+	/** True if section data has been endian swapped */
+	uint8_t *shdrs_host_endian;
+
+	size_t shdr_idx_symtab;
+
+	struct nfp_elf_elf64_sym *syms;
+	size_t syms_cnt;
+
+	char *shstrtab;
+	size_t shstrtab_sz;
+
+	char *symstrtab;
+	size_t symstrtab_sz;
+
+	struct nfp_elf_elf_meconfig *meconfs;
+	size_t meconfs_cnt;
+
+	/* ==== .note data start ==== */
+
+	/**
+	 * Following data derived from SHT_NOTE sections for read-only usage.
+	 * These fields are not used in nfp_elf_to_buf()
+	 */
+	int rev_min; /**< -1 if file did not specify */
+	int rev_max; /**< -1 if file did not specify */
+
+	/**
+	 * If mip_shndx == 0 and mip_sh_off == 0, the .note stated there is no MIP.
+	 * If mip_shndx == 0 and mip_sh_off == UINT64_MAX, there was no .note and
+	 * a MIP _may_ still be found in the first 256KiB of DRAM/EMEM data.
+	 */
+	size_t mip_shndx; /**< Section in which MIP resides, 0 if no MIP */
+	uint64_t mip_sh_off; /**< Offset within section (not address) */
+
+	struct nfp_elf_fw_mip fw_mip;
+	const char *fw_info_strtab;
+	size_t fw_info_strtab_sz;
+
+	/* ==== .note.user data start ==== */
+	size_t user_note_cnt;
+	struct nfp_elf_user_note *user_notes;
+
+	void *dbgdata;
+
+	int family;
+
+	/**
+	 * For const entry points in the API, we allocate and keep a buffer
+	 * and for mutable entry points we assume the buffer remains valid
+	 * and we just set pointers to it.
+	 */
+	void *_buf;
+	size_t _bufsz;
+};
+
+static void
+nfp_elf_free(struct nfp_elf *ectx)
+{
+	if (ectx == NULL)
+		return;
+
+	free(ectx->shdrs);
+	free(ectx->shdrs_data);
+	free(ectx->shdrs_host_endian);
+	if (ectx->_bufsz != 0)
+		free(ectx->_buf);
+
+	free(ectx);
+}
+
+static size_t
+nfp_elf_get_sec_ent_cnt(struct nfp_elf *ectx,
+		size_t idx)
+{
+	uint64_t sh_size = rte_le_to_cpu_64(ectx->shdrs[idx].sh_size);
+	uint64_t sh_entsize = rte_le_to_cpu_64(ectx->shdrs[idx].sh_entsize);
+
+	if (sh_entsize != 0)
+		return sh_size / sh_entsize;
+
+	return 0;
+}
+
+static bool
+nfp_elf_check_sh_size(uint64_t sh_size)
+{
+	if (sh_size == 0 || sh_size > UINT32_MAX)
+		return false;
+
+	return true;
+}
+
+static const char *
+nfp_elf_fwinfo_next(struct nfp_elf *ectx,
+		const char *key_val)
+{
+	size_t s_len;
+	const char *strtab = ectx->fw_info_strtab;
+	ssize_t tab_sz = (ssize_t)ectx->fw_info_strtab_sz;
+
+	if (key_val == NULL)
+		return strtab;
+
+	s_len = strlen(key_val);
+	if (key_val < strtab || ((key_val + s_len + 1) >= (strtab + tab_sz - 1)))
+		return NULL;
+
+	key_val += s_len + 1;
+
+	return key_val;
+}
+
+static const char *
+nfp_elf_fwinfo_lookup(struct nfp_elf *ectx,
+		const char *key)
+{
+	size_t s_len;
+	const char *s;
+	size_t key_len = strlen(key);
+	const char *strtab = ectx->fw_info_strtab;
+	ssize_t tab_sz = (ssize_t)ectx->fw_info_strtab_sz;
+
+	if (strtab == NULL)
+		return NULL;
+
+	for (s = strtab, s_len = strlen(s) + 1;
+			(s[0] != '\0') && (tab_sz > 0);
+			s_len = strlen(s) + 1, tab_sz -= s_len, s += s_len) {
+		if ((strncmp(s, key, key_len) == 0) && (s[key_len] == '='))
+			return &s[key_len + 1];
+	}
+
+	return NULL;
+}
+
+static bool
+nfp_elf_arch_is_thornham(struct nfp_elf *ectx)
+{
+	if (ectx == NULL)
+		return false;
+
+	if (ectx->family == NFP_CHIP_FAMILY_NFP6000 || ectx->family == NFP_CHIP_FAMILY_NFP3800)
+		return true;
+
+	return false;
+}
+
+static int
+nfp_elf_parse_sht_rel(struct nfp_elf_elf64_shdr *sec,
+		struct nfp_elf *ectx,
+		size_t idx,
+		uint8_t *buf8)
+{
+	uint64_t sh_size = rte_le_to_cpu_64(sec->sh_size);
+	uint64_t sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+	uint64_t sh_entsize = rte_le_to_cpu_64(sec->sh_entsize);
+
+	if (sh_entsize != sizeof(struct nfp_elf_elf64_rel)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	if (!nfp_elf_check_sh_size(sh_size)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	ectx->shdrs_data[idx] = buf8 + sh_offset;
+	ectx->shdrs_host_endian[idx] = 1;
+
+	return 0;
+}
+
+static int
+nfp_elf_parse_note_name_nfp(struct nfp_elf *ectx,
+		size_t idx,
+		uint32_t ndescsz,
+		uint32_t ntype,
+		const char *nname,
+		rte_le32_t *descword)
+{
+	if (strncmp(nname, NFP_ELT_NOTE_NAME_NFP, NFP_ELT_NOTE_NAME_NFP_SZ) == 0) {
+		switch (ntype) {
+		case NFP_ELF_NT_NFP_REVS:
+			if (ndescsz != 8) {
+				PMD_DRV_LOG(ERR, "Invalid ELF NOTE descsz in section %zu.", idx);
+				return -EINVAL;
+			}
+
+			ectx->rev_min = (int)rte_le_to_cpu_32(descword[0]);
+			ectx->rev_max = (int)rte_le_to_cpu_32(descword[1]);
+			break;
+		case NFP_ELF_NT_NFP_MIP_LOCATION:
+			if (ndescsz != 12) {
+				PMD_DRV_LOG(ERR, "Invalid ELF NOTE descsz in section %zu.", idx);
+				return -EINVAL;
+			}
+
+			ectx->mip_shndx = rte_le_to_cpu_32(descword[0]);
+			if (ectx->mip_shndx == 0) {
+				ectx->mip_sh_off = 0;
+				break;
+			}
+
+			if (ectx->mip_shndx >= ectx->shdrs_cnt) {
+				PMD_DRV_LOG(ERR, "Invalid ELF NOTE shndx in section %zu.", idx);
+				return -EINVAL;
+			}
+
+			ectx->mip_sh_off = rte_le_to_cpu_32(descword[1]) |
+					(uint64_t)rte_le_to_cpu_32(descword[2]) << 32;
+			break;
+		default:
+			break;
+		}
+	} else if (strncmp(nname, NFP_ELT_NOTE_NAME_NFP_USER,
+			NFP_ELT_NOTE_NAME_NFP_USER_SZ) == 0 && ntype == NFP_ELF_NT_NFP_USER) {
+		ectx->user_note_cnt++;
+	}
+
+	return 0;
+}
+
+static int
+nfp_elf_parse_sht_note(struct nfp_elf_elf64_shdr *sec,
+		struct nfp_elf *ectx,
+		size_t idx,
+		uint8_t *buf8)
+{
+	int err;
+	size_t nsz;
+	uint8_t *desc;
+	uint32_t ntype;
+	uint32_t nnamesz;
+	uint32_t ndescsz;
+	const char *nname;
+	uint8_t *shdrs_data;
+	rte_le32_t *descword;
+	struct nfp_elf_elf64_nhdr *nhdr;
+	struct nfp_elf_user_note *unote;
+	uint64_t sh_size = rte_le_to_cpu_64(sec->sh_size);
+	uint64_t sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+
+	if (!nfp_elf_check_sh_size(sh_size)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	shdrs_data = buf8 + sh_offset;
+	ectx->shdrs_data[idx] = shdrs_data;
+	ectx->shdrs_host_endian[idx] = 0;
+
+	/* Extract notes that we recognise */
+	nhdr = (struct nfp_elf_elf64_nhdr *)shdrs_data;
+
+	while ((uint8_t *)nhdr < (shdrs_data + sh_size)) {
+		nnamesz  = rte_le_to_cpu_32(nhdr->n_namesz);
+		ndescsz  = rte_le_to_cpu_32(nhdr->n_descsz);
+		ntype    = rte_le_to_cpu_32(nhdr->n_type);
+		nname    = (const char *)((uint8_t *)nhdr + sizeof(*nhdr));
+		descword = (rte_le32_t *)((uint8_t *)nhdr + sizeof(*nhdr) +
+				((nnamesz + UINT32_C(3)) & ~UINT32_C(3)));
+
+		err = nfp_elf_parse_note_name_nfp(ectx, idx, ndescsz, ntype, nname, descword);
+		if (err != 0)
+			return err;
+
+		nhdr = (struct nfp_elf_elf64_nhdr *)((uint8_t *)descword +
+				((ndescsz + UINT32_C(3)) & ~UINT32_C(3)));
+	}
+
+	if (ectx->user_note_cnt == 0)
+		return 0;
+
+	ectx->user_notes = calloc(ectx->user_note_cnt, sizeof(*ectx->user_notes));
+	if (ectx->user_notes == NULL) {
+		PMD_DRV_LOG(ERR, "Out of memory.");
+		return -ENOMEM;
+	}
+
+	nhdr = (struct nfp_elf_elf64_nhdr *)shdrs_data;
+	unote = ectx->user_notes;
+	while ((uint8_t *)nhdr < (shdrs_data + sh_size)) {
+		nnamesz = rte_le_to_cpu_32(nhdr->n_namesz);
+		ndescsz = rte_le_to_cpu_32(nhdr->n_descsz);
+		ntype   = rte_le_to_cpu_32(nhdr->n_type);
+		nname   = (const char *)((uint8_t *)nhdr + sizeof(*nhdr));
+		desc    = (uint8_t *)nhdr + sizeof(*nhdr) +
+				((nnamesz + UINT32_C(3)) & ~UINT32_C(3));
+
+		if (strncmp(nname, NFP_ELT_NOTE_NAME_NFP_USER,
+				NFP_ELT_NOTE_NAME_NFP_USER_SZ) != 0)
+			continue;
+
+		if (ntype != NFP_ELF_NT_NFP_USER)
+			continue;
+
+		unote->name = (const char *)desc;
+		nsz = strlen(unote->name) + 1;
+		if (nsz % 4 != 0)
+			nsz = ((nsz / 4) + 1) * 4;
+		if (nsz > ndescsz) {
+			PMD_DRV_LOG(ERR, "Invalid ELF USER NOTE descsz in section %zu.", idx);
+			return -EINVAL;
+		}
+
+		unote->data_sz = ndescsz - (uint32_t)nsz;
+		if (unote->data_sz != 0)
+			unote->data = desc + nsz;
+		unote++;
+
+		nhdr = (struct nfp_elf_elf64_nhdr *)
+				(desc + ((ndescsz + UINT32_C(3)) & ~UINT32_C(3)));
+	}
+
+	return 0;
+}
+
+static int
+nfp_elf_parse_sht_meconfig(struct nfp_elf_elf64_shdr *sec,
+		struct nfp_elf *ectx,
+		size_t idx,
+		uint8_t *buf8)
+{
+	size_t ent_cnt;
+	uint8_t *shdrs_data;
+	uint64_t sh_size = rte_le_to_cpu_64(sec->sh_size);
+	uint64_t sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+
+	if (!nfp_elf_check_sh_size(sh_size)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	shdrs_data = buf8 + sh_offset;
+	ent_cnt = nfp_elf_get_sec_ent_cnt(ectx, idx);
+	ectx->shdrs_data[idx] = shdrs_data;
+	ectx->meconfs = (struct nfp_elf_elf_meconfig *)shdrs_data;
+	ectx->meconfs_cnt = ent_cnt;
+	ectx->shdrs_host_endian[idx] = 1;
+
+	return 0;
+}
+
+static int
+nfp_elf_parse_sht_initreg(struct nfp_elf_elf64_shdr *sec,
+		struct nfp_elf *ectx,
+		size_t idx,
+		uint8_t *buf8)
+{
+	uint64_t sh_size = rte_le_to_cpu_64(sec->sh_size);
+	uint64_t sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+	uint64_t sh_entsize = rte_le_to_cpu_64(sec->sh_entsize);
+
+	if (!nfp_elf_arch_is_thornham(ectx)) {
+		PMD_DRV_LOG(ERR, "Section not supported for target arch.");
+		return -ENOTSUP;
+	}
+
+	if (sh_entsize != sizeof(struct nfp_elf_elf_initregentry) ||
+			!nfp_elf_check_sh_size(sh_size)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	ectx->shdrs_data[idx] = buf8 + sh_offset;
+	ectx->shdrs_host_endian[idx] = 1;
+
+	return 0;
+}
+
+static int
+nfp_elf_parse_sht_symtab(struct nfp_elf_elf64_shdr *sec,
+		struct nfp_elf *ectx,
+		size_t idx,
+		uint8_t *buf8)
+{
+	uint64_t sh_size = rte_le_to_cpu_64(sec->sh_size);
+	uint64_t sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+	uint64_t sh_entsize = rte_le_to_cpu_64(sec->sh_entsize);
+
+	if (sh_entsize != sizeof(struct nfp_elf_elf64_sym) ||
+			!nfp_elf_check_sh_size(sh_size)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+		return -EINVAL;
+	}
+
+	ectx->shdrs_data[idx] = buf8 + sh_offset;
+	ectx->shdrs_host_endian[ectx->shdr_idx_symtab] = 1;
+
+	return 0;
+}
+
+static int
+nfp_elf_populate_fw_mip(struct nfp_elf *ectx,
+		uint8_t *buf8)
+{
+	uint8_t *pu8;
+	const char *nx;
+	uint64_t sh_size;
+	uint64_t sh_offset;
+	uint32_t first_entry;
+	const struct nfp_mip *mip;
+	struct nfp_elf_elf64_shdr *sec;
+	const struct nfp_mip_entry *ent;
+	const struct nfp_mip_fwinfo_entry *fwinfo;
+
+	sec = &ectx->shdrs[ectx->mip_shndx];
+	sh_size = rte_le_to_cpu_64(sec->sh_size);
+	sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+	pu8 = buf8 + sh_offset + ectx->mip_sh_off;
+	mip = (const struct nfp_mip *)pu8;
+	first_entry = rte_le_to_cpu_32(mip->first_entry);
+
+	if (mip->signature != NFP_MIP_SIGNATURE) {
+		PMD_DRV_LOG(ERR, "Incorrect MIP signature %#08x",
+				rte_le_to_cpu_32(mip->signature));
+		return -EINVAL;
+	}
+
+	ectx->fw_mip.shndx = ectx->mip_shndx;
+	ectx->fw_mip.sh_offset = ectx->mip_sh_off;
+	ectx->fw_mip.mip_ver = mip->mip_version;
+
+	if (ectx->fw_mip.mip_ver != NFP_MIP_VERSION) {
+		PMD_DRV_LOG(ERR, "MIP note pointer does not point to recognised version.");
+		return -EINVAL;
+	}
+
+	ectx->fw_mip.fw_version   = mip->version;
+	ectx->fw_mip.fw_buildnum  = mip->buildnum;
+	ectx->fw_mip.fw_buildtime = mip->buildtime;
+	strncpy(ectx->fw_mip.fw_name, mip->name, 16);
+
+	/*
+	 * If there is a FWINFO v1 entry, it will be first and
+	 * right after the MIP itself, so in the same section.
+	 */
+	if (ectx->mip_sh_off + first_entry + sizeof(*ent) < sh_size) {
+		pu8 += first_entry;
+		ent = (const struct nfp_mip_entry *)pu8;
+		if (ent->type == NFP_MIP_TYPE_FWINFO && ent->version == 1) {
+			pu8 += sizeof(*ent);
+			fwinfo = (const struct nfp_mip_fwinfo_entry *)pu8;
+			if (fwinfo->kv_len != 0) {
+				ectx->fw_info_strtab_sz = fwinfo->kv_len;
+				ectx->fw_info_strtab = fwinfo->key_value_strs;
+			}
+		}
+	}
+
+	ectx->fw_mip.fw_typeid = nfp_elf_fwinfo_lookup(ectx, "TypeId");
+
+	/*
+	 * TypeId will be the last reserved key-value pair, so skip
+	 * to the first entry after it for the user values.
+	 */
+	if (ectx->fw_mip.fw_typeid == NULL)
+		return 0;
+
+	nx = nfp_elf_fwinfo_next(ectx, ectx->fw_mip.fw_typeid);
+	if (nx == NULL)
+		ectx->fw_info_strtab_sz = 0;
+	else
+		ectx->fw_info_strtab_sz -= (nx - ectx->fw_info_strtab);
+	ectx->fw_info_strtab = nx;
+
+	return 0;
+}
+
+static int
+nfp_elf_read_file_headers(struct nfp_elf *ectx,
+		void *buf)
+{
+	uint16_t e_type;
+	uint32_t e_flags;
+	uint32_t e_version;
+	uint16_t e_machine;
+
+	ectx->ehdr = buf;
+	e_type = rte_le_to_cpu_16(ectx->ehdr->e_type);
+	e_flags = rte_le_to_cpu_32(ectx->ehdr->e_flags);
+	e_version = rte_le_to_cpu_32(ectx->ehdr->e_version);
+	e_machine = rte_le_to_cpu_16(ectx->ehdr->e_machine);
+
+	switch (e_machine) {
+	case NFP_ELF_EM_NFP:
+		ectx->family = (e_flags >> NFP_ELF_EF_NFP_FAMILY_LSB)
+				& NFP_ELF_EF_NFP_FAMILY_MASK;
+		break;
+	case NFP_ELF_EM_NFP6000:
+		ectx->family = NFP_CHIP_FAMILY_NFP6000;
+		break;
+	default:
+		PMD_DRV_LOG(ERR, "Invalid ELF machine type.");
+		return -EINVAL;
+	}
+
+	if ((e_type != NFP_ELF_ET_EXEC && e_type != NFP_ELF_ET_REL &&
+			e_type != NFP_ELF_ET_NFP_PARTIAL_EXEC &&
+			e_type != NFP_ELF_ET_NFP_PARTIAL_REL) ||
+			e_version != NFP_ELF_EV_CURRENT ||
+			ectx->ehdr->e_ehsize != sizeof(struct nfp_elf_elf64_ehdr) ||
+			ectx->ehdr->e_shentsize != sizeof(struct nfp_elf_elf64_shdr)) {
+		PMD_DRV_LOG(ERR, "Invalid ELF file header.");
+		return -EINVAL;
+	}
+
+	if (ectx->ehdr->e_shoff < ectx->ehdr->e_ehsize) {
+		PMD_DRV_LOG(ERR, "Invalid ELF header content.");
+		return -EINVAL;
+	}
+
+	if (ectx->ehdr->e_shstrndx >= ectx->ehdr->e_shnum) {
+		PMD_DRV_LOG(ERR, "Invalid ELF header content.");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+nfp_elf_read_section_headers(struct nfp_elf *ectx,
+		uint8_t *buf8,
+		size_t buf_len)
+{
+	size_t idx;
+	int err = 0;
+	uint8_t *pu8;
+	uint64_t sh_size;
+	uint64_t sh_offset;
+	uint64_t sh_entsize;
+	struct nfp_elf_elf64_shdr *sec;
+	uint64_t e_shoff = rte_le_to_cpu_16(ectx->ehdr->e_shoff);
+	uint16_t e_shnum = rte_le_to_cpu_16(ectx->ehdr->e_shnum);
+
+	if (buf_len < e_shoff + ((size_t)e_shnum * sizeof(*sec))) {
+		PMD_DRV_LOG(ERR, "ELF data too short.");
+		return -EINVAL;
+	}
+
+	pu8 = buf8 + e_shoff;
+
+	if (e_shnum == 0) {
+		ectx->shdrs = NULL;
+		ectx->shdrs_data = NULL;
+		ectx->shdrs_host_endian = NULL;
+		ectx->shdrs_cnt = 0;
+		return 0;
+	}
+
+	ectx->shdrs = calloc(e_shnum, sizeof(*ectx->shdrs));
+	if (ectx->shdrs == NULL) {
+		PMD_DRV_LOG(ERR, "Out of memory.");
+		return -ENOMEM;
+	}
+
+	ectx->shdrs_data = calloc(e_shnum, sizeof(void *));
+	if (ectx->shdrs_data == NULL) {
+		PMD_DRV_LOG(ERR, "Out of memory.");
+		err = -ENOMEM;
+		goto free_shdrs;
+	}
+
+	ectx->shdrs_host_endian = calloc(e_shnum, sizeof(ectx->shdrs_host_endian[0]));
+	if (ectx->shdrs_host_endian == NULL) {
+		PMD_DRV_LOG(ERR, "Out of memory.");
+		err = -ENOMEM;
+		goto free_shdrs_data;
+	}
+
+	memcpy(ectx->shdrs, pu8, e_shnum * sizeof(*ectx->shdrs));
+	ectx->shdrs_cnt = e_shnum;
+
+	for (idx = 0, sec = ectx->shdrs; idx < ectx->shdrs_cnt; idx++, sec++) {
+		sh_size = rte_le_to_cpu_64(sec->sh_size);
+		sh_offset = rte_le_to_cpu_64(sec->sh_offset);
+		sh_entsize = rte_le_to_cpu_64(sec->sh_entsize);
+
+		if (sh_entsize != 0 && (sh_size % sh_entsize != 0)) {
+			PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+			err = -EINVAL;
+			goto free_shdrs_host_endian;
+		}
+
+		switch (rte_le_to_cpu_32(sec->sh_type)) {
+		case NFP_ELF_SHT_REL:
+			err = nfp_elf_parse_sht_rel(sec, ectx, idx, buf8);
+			if (err != 0) {
+				PMD_DRV_LOG(ERR, "Failed to parse sht rel.");
+				goto free_shdrs_host_endian;
+			}
+			break;
+		case NFP_ELF_SHT_NOTE:
+			err = nfp_elf_parse_sht_note(sec, ectx, idx, buf8);
+			if (err != 0) {
+				PMD_DRV_LOG(ERR, "Failed to parse sht note.");
+				goto free_shdrs_host_endian;
+			}
+			break;
+		case NFP_ELF_SHT_NFP_MECONFIG:
+			err = nfp_elf_parse_sht_meconfig(sec, ectx, idx, buf8);
+			if (err != 0) {
+				PMD_DRV_LOG(ERR, "Failed to parse sht meconfig.");
+				goto free_shdrs_host_endian;
+			}
+			break;
+		case NFP_ELF_SHT_NFP_INITREG:
+			err = nfp_elf_parse_sht_initreg(sec, ectx, idx, buf8);
+			if (err != 0) {
+				PMD_DRV_LOG(ERR, "Failed to parse sht initregp.");
+				goto free_shdrs_host_endian;
+			}
+			break;
+		case NFP_ELF_SHT_SYMTAB:
+			err = nfp_elf_parse_sht_symtab(sec, ectx, idx, buf8);
+			if (err != 0) {
+				PMD_DRV_LOG(ERR, "Failed to parse sht symtab.");
+				goto free_shdrs_host_endian;
+			}
+			break;
+		case NFP_ELF_SHT_NOBITS:
+		case NFP_ELF_SHT_NULL:
+			break;
+		default:
+			if (sh_offset > 0 && sh_size <= 0)
+				break;
+
+			/*
+			 * Limit sections to 4GiB, because they won't need to be this large
+			 * and this ensures we can handle the file on 32-bit hosts without
+			 * unexpected problems.
+			 */
+			if (sh_size > UINT32_MAX) {
+				PMD_DRV_LOG(ERR, "Invalid ELF section header, index %zu.", idx);
+				err = -EINVAL;
+				goto free_shdrs_host_endian;
+			}
+
+			pu8 = buf8 + sh_offset;
+			ectx->shdrs_data[idx] = pu8;
+			ectx->shdrs_host_endian[idx] = 0;
+			break;
+		}
+	}
+
+	return 0;
+
+free_shdrs_host_endian:
+	free(ectx->shdrs_host_endian);
+free_shdrs_data:
+	free(ectx->shdrs_data);
+free_shdrs:
+	free(ectx->shdrs);
+
+	return err;
+}
+
+static int
+nfp_elf_read_shstrtab(struct nfp_elf *ectx)
+{
+	struct nfp_elf_elf64_shdr *sec;
+	uint16_t e_shstrndx = rte_le_to_cpu_16(ectx->ehdr->e_shstrndx);
+
+	if (ectx->ehdr->e_shnum <= ectx->ehdr->e_shstrndx) {
+		PMD_DRV_LOG(ERR, "Invalid Index.");
+		return -EINVAL;
+	}
+
+	sec = &ectx->shdrs[e_shstrndx];
+	if (sec == NULL || rte_le_to_cpu_32(sec->sh_type) != NFP_ELF_SHT_STRTAB) {
+		PMD_DRV_LOG(ERR, "Invalid ELF shstrtab.");
+		return -EINVAL;
+	}
+
+	ectx->shstrtab = ectx->shdrs_data[e_shstrndx];
+	ectx->shstrtab_sz = rte_le_to_cpu_64(sec->sh_size);
+
+	return 0;
+}
+
+static int
+nfp_elf_read_first_symtab(struct nfp_elf *ectx)
+{
+	size_t idx;
+	uint32_t sh_type;
+	uint64_t sh_size;
+	struct nfp_elf_elf64_shdr *sec;
+
+	for (idx = 0, sec = ectx->shdrs; idx < ectx->shdrs_cnt; idx++, sec++) {
+		if (sec != NULL) {
+			sh_type = rte_le_to_cpu_32(sec->sh_type);
+			if (sh_type == NFP_ELF_SHT_SYMTAB)
+				break;
+		}
+	}
+
+	sh_size = rte_le_to_cpu_64(sec->sh_size);
+
+	if (idx < ectx->shdrs_cnt && sh_type == NFP_ELF_SHT_SYMTAB) {
+		ectx->shdr_idx_symtab = idx;
+		ectx->syms = ectx->shdrs_data[idx];
+		ectx->syms_cnt = nfp_elf_get_sec_ent_cnt(ectx, idx);
+
+		/* Load symtab's strtab */
+		idx = rte_le_to_cpu_32(sec->sh_link);
+
+		if (idx == NFP_ELF_SHN_UNDEF || idx >= ectx->shdrs_cnt) {
+			PMD_DRV_LOG(ERR, "ELF symtab has no strtab.");
+			return -EINVAL;
+		}
+
+		sec = &ectx->shdrs[idx];
+		sh_type = rte_le_to_cpu_32(sec->sh_type);
+		if (sh_type != NFP_ELF_SHT_STRTAB) {
+			PMD_DRV_LOG(ERR, "ELF symtab has no strtab.");
+			return -EINVAL;
+		}
+
+		if (!nfp_elf_check_sh_size(sh_size)) {
+			PMD_DRV_LOG(ERR, "ELF symtab has invalid strtab.");
+			return -EINVAL;
+		}
+
+		ectx->symstrtab = ectx->shdrs_data[idx];
+		ectx->symstrtab_sz = sh_size;
+	}
+
+	return 0;
+}
+
+static int
+nfp_elf_is_valid_file(uint8_t *buf8)
+{
+	if (buf8[NFP_ELF_EI_MAG0] != NFP_ELF_ELFMAG0 ||
+			buf8[NFP_ELF_EI_MAG1] != NFP_ELF_ELFMAG1 ||
+			buf8[NFP_ELF_EI_MAG2] != NFP_ELF_ELFMAG2 ||
+			buf8[NFP_ELF_EI_MAG3] != NFP_ELF_ELFMAG3 ||
+			buf8[NFP_ELF_EI_VERSION] != NFP_ELF_EV_CURRENT ||
+			buf8[NFP_ELF_EI_DATA] != NFP_ELF_ELFDATA2LSB)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+nfp_elf_is_valid_class(uint8_t *buf8)
+{
+	if (buf8[NFP_ELF_EI_CLASS] != NFP_ELF_ELFCLASS64)
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct nfp_elf *
+nfp_elf_mutable_buf(void *buf,
+		size_t buf_len)
+{
+	int err = 0;
+	uint8_t *buf8 = buf;
+	struct nfp_elf *ectx;
+
+	if (buf == NULL) {
+		PMD_DRV_LOG(ERR, "Invalid parameters.");
+		return NULL;
+	}
+
+	if (buf_len < sizeof(struct nfp_elf_elf64_ehdr)) {
+		PMD_DRV_LOG(ERR, "ELF data too short.");
+		return NULL;
+	}
+
+	ectx = calloc(1, sizeof(struct nfp_elf));
+	if (ectx == NULL) {
+		PMD_DRV_LOG(ERR, "Out of memory.");
+		return NULL;
+	}
+
+	ectx->rev_min = -1;
+	ectx->rev_max = -1;
+	ectx->mip_sh_off = UINT64_MAX;
+
+	err = nfp_elf_is_valid_file(buf8);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Not a valid ELF file.");
+		goto elf_free;
+	}
+
+	err = nfp_elf_is_valid_class(buf8);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Unknown ELF class.");
+		goto elf_free;
+	}
+
+	err = nfp_elf_read_file_headers(ectx, buf);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Failed to read file headers.");
+		goto elf_free;
+	}
+
+	err = nfp_elf_read_section_headers(ectx, buf8, buf_len);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Failed to read section headers.");
+		goto elf_free;
+	}
+
+	err = nfp_elf_read_shstrtab(ectx);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Failed to read shstrtab.");
+		goto elf_free;
+	}
+
+	/* Read first symtab if any, assuming it's the primary or only one */
+	err = nfp_elf_read_first_symtab(ectx);
+	if (err != 0) {
+		PMD_DRV_LOG(ERR, "Failed to read first symtab.");
+		goto elf_free;
+	}
+
+	/* Populate the fw_mip struct if we have a .note for it */
+	if (ectx->mip_shndx != 0) {
+		err = nfp_elf_populate_fw_mip(ectx, buf8);
+		if (err != 0) {
+			PMD_DRV_LOG(ERR, "Failed to populate the fw mip.");
+			goto elf_free;
+		}
+	}
+
+	ectx->_buf = buf;
+	ectx->_bufsz = 0;
+
+	return ectx;
+
+elf_free:
+	nfp_elf_free(ectx);
+
+	return NULL;
+}
+
+int
+nfp_elf_get_fw_version(uint32_t *fw_version,
+		char *fw_name)
+{
+	void *fw_buf;
+	size_t fsize;
+	struct nfp_elf *elf;
+
+	if (rte_firmware_read(fw_name, &fw_buf, &fsize) != 0) {
+		PMD_DRV_LOG(ERR, "firmware %s not found!", fw_name);
+		return -ENOENT;
+	}
+
+	elf = nfp_elf_mutable_buf(fw_buf, fsize);
+	if (elf == NULL) {
+		PMD_DRV_LOG(ERR, "Parse nffw file failed.");
+		free(fw_buf);
+		return -EIO;
+	}
+
+	*fw_version = rte_le_to_cpu_32(elf->fw_mip.fw_version);
+
+	nfp_elf_free(elf);
+	free(fw_buf);
+	return 0;
+}
+
diff --git a/drivers/net/nfp/nfpcore/nfp_elf.h b/drivers/net/nfp/nfpcore/nfp_elf.h
new file mode 100644
index 0000000000..4081af6f01
--- /dev/null
+++ b/drivers/net/nfp/nfpcore/nfp_elf.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Corigine, Inc.
+ * All rights reserved.
+ */
+
+#ifndef __NFP_ELF_H__
+#define __NFP_ELF_H__
+
+#include <stdint.h>
+
+int nfp_elf_get_fw_version(uint32_t *fw_version, char *fw_name);
+
+#endif /* __NFP_ELF_H__ */
diff --git a/drivers/net/nfp/nfpcore/nfp_mip.c b/drivers/net/nfp/nfpcore/nfp_mip.c
index d5ada3687a..98d1d19047 100644
--- a/drivers/net/nfp/nfpcore/nfp_mip.c
+++ b/drivers/net/nfp/nfpcore/nfp_mip.c
@@ -10,30 +10,6 @@
 #include "nfp_logs.h"
 #include "nfp_nffw.h"
 
-#define NFP_MIP_SIGNATURE        rte_cpu_to_le_32(0x0050494d)  /* "MIP\0" */
-#define NFP_MIP_VERSION          rte_cpu_to_le_32(1)
-#define NFP_MIP_MAX_OFFSET       (256 * 1024)
-
-struct nfp_mip {
-	uint32_t signature;
-	uint32_t mip_version;
-	uint32_t mip_size;
-	uint32_t first_entry;
-
-	uint32_t version;
-	uint32_t buildnum;
-	uint32_t buildtime;
-	uint32_t loadtime;
-
-	uint32_t symtab_addr;
-	uint32_t symtab_size;
-	uint32_t strtab_addr;
-	uint32_t strtab_size;
-
-	char name[16];
-	char toolchain[32];
-};
-
 /* Read memory and check if it could be a valid MIP */
 static int
 nfp_mip_try_read(struct nfp_cpp *cpp,
@@ -134,6 +110,12 @@ nfp_mip_name(const struct nfp_mip *mip)
 	return mip->name;
 }
 
+uint32_t
+nfp_mip_fw_version(const struct nfp_mip *mip)
+{
+	return rte_le_to_cpu_32(mip->version);
+}
+
 /**
  * Get the address and size of the MIP symbol table.
  *
diff --git a/drivers/net/nfp/nfpcore/nfp_mip.h b/drivers/net/nfp/nfpcore/nfp_mip.h
index dbd9af31ed..411fe413d7 100644
--- a/drivers/net/nfp/nfpcore/nfp_mip.h
+++ b/drivers/net/nfp/nfpcore/nfp_mip.h
@@ -8,12 +8,80 @@
 
 #include "nfp_cpp.h"
 
-struct nfp_mip;
+/* "MIP\0" */
+#define NFP_MIP_SIGNATURE        rte_cpu_to_le_32(0x0050494d)
+#define NFP_MIP_VERSION          rte_cpu_to_le_32(1)
+
+/* nfp_mip_entry_type */
+#define NFP_MIP_TYPE_FWINFO      0x70000002
+
+/* Each packed struct field is stored as Little Endian */
+struct nfp_mip {
+	rte_le32_t signature;
+	rte_le32_t mip_version;
+
+	rte_le32_t mip_size;
+	rte_le32_t first_entry;
+
+	rte_le32_t version;
+	rte_le32_t buildnum;
+	rte_le32_t buildtime;
+	rte_le32_t loadtime;
+
+	rte_le32_t symtab_addr;
+	rte_le32_t symtab_size;
+	rte_le32_t strtab_addr;
+	rte_le32_t strtab_size;
+
+	char name[16];
+	char toolchain[32];
+};
+
+struct nfp_mip_entry {
+	uint32_t type;
+	uint32_t version;
+	uint32_t offset_next;
+};
+
+/*
+ * A key-value pair has no imposed limit, but it is recommended that
+ * consumers only allocate enough memory for keys they plan to process and
+ * skip over unused keys or ignore values that are longer than expected.
+ *
+ * For MIPv1, this will be preceded by struct nfp_mip_entry.
+ * The entry size will be the size of key_value_strs, round to the next
+ * 4-byte multiple. If entry size is 0, then there are no key-value strings
+ * and it will not contain an empty string.
+ *
+ * The following keys are reserved and possibly set by the linker. The
+ * convention is to start linker-set keys with a capital letter. Reserved
+ * entries will be placed first in key_value_strs, user entries will be
+ * placed next and be sorted alphabetically.
+ * TypeId - Present if a user specified fw_typeid when linking.
+ *
+ * The following keys are reserved, but not used. Their values are in the
+ * root MIP struct.
+ */
+struct nfp_mip_fwinfo_entry {
+	/** The byte size of @p key_value_strs. */
+	uint32_t kv_len;
+
+	/** The number of key-value pairs in the following string. */
+	uint32_t num;
+
+	/**
+	 * A series of NUL terminated strings, terminated by an extra
+	 * NUL which is also the last byte of the entry, so an iterator
+	 * can either check on size or when key[0] == '\0'.
+	 */
+	char key_value_strs[];
+};
 
 struct nfp_mip *nfp_mip_open(struct nfp_cpp *cpp);
 void nfp_mip_close(struct nfp_mip *mip);
 
 const char *nfp_mip_name(const struct nfp_mip *mip);
+uint32_t nfp_mip_fw_version(const struct nfp_mip *mip);
 void nfp_mip_symtab(const struct nfp_mip *mip, uint32_t *addr, uint32_t *size);
 void nfp_mip_strtab(const struct nfp_mip *mip, uint32_t *addr, uint32_t *size);
 
-- 
2.39.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 2/3] net/nfp: reload the firmware only when firmware changed
  2024-03-01  8:42 ` [PATCH v2 0/3] reload the firmware as needed Chaoyong He
  2024-03-01  8:42   ` [PATCH v2 1/3] net/nfp: add the elf module Chaoyong He
@ 2024-03-01  8:42   ` Chaoyong He
  2024-03-01  8:42   ` [PATCH v2 3/3] net/nfp: add force reload firmware option Chaoyong He
  2024-03-04 13:28   ` [PATCH v2 0/3] reload the firmware as needed Ferruh Yigit
  3 siblings, 0 replies; 17+ messages in thread
From: Chaoyong He @ 2024-03-01  8:42 UTC (permalink / raw)
  To: dev; +Cc: oss-drivers, Peng Zhang, Chaoyong He, Long Wu

From: Peng Zhang <peng.zhang@corigine.com>

Add the interfaces of getting firmware version from BSP and ELF file,
only reloading the firmware when the version is different, which
means the firmware has changed.

This will accelerate the average startup time for both
multi-PF and single-PF firmware.

Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
Reviewed-by: Long Wu <long.wu@corigine.com>
---
 drivers/net/nfp/nfp_ethdev.c     | 85 +++++++++++++++++++++++++++-----
 drivers/net/nfp/nfp_net_common.c | 17 +++++++
 drivers/net/nfp/nfp_net_common.h |  2 +
 3 files changed, 93 insertions(+), 11 deletions(-)

diff --git a/drivers/net/nfp/nfp_ethdev.c b/drivers/net/nfp/nfp_ethdev.c
index 3ece806487..902a62a86b 100644
--- a/drivers/net/nfp/nfp_ethdev.c
+++ b/drivers/net/nfp/nfp_ethdev.c
@@ -14,6 +14,7 @@
 #include "nfd3/nfp_nfd3.h"
 #include "nfdk/nfp_nfdk.h"
 #include "nfpcore/nfp_cpp.h"
+#include "nfpcore/nfp_elf.h"
 #include "nfpcore/nfp_hwinfo.h"
 #include "nfpcore/nfp_rtsym.h"
 #include "nfpcore/nfp_nsp.h"
@@ -1070,6 +1071,35 @@ nfp_fw_unload(struct nfp_cpp *cpp)
 	nfp_nsp_close(nsp);
 }
 
+static int
+nfp_fw_check_change(struct nfp_cpp *cpp,
+		char *fw_name,
+		bool *fw_changed)
+{
+	int ret;
+	struct nfp_net_hw hw;
+	uint32_t new_version = 0;
+	uint32_t old_version = 0;
+
+	ret = nfp_elf_get_fw_version(&new_version, fw_name);
+	if (ret != 0)
+		return ret;
+
+	hw.cpp = cpp;
+	nfp_net_get_fw_version(&hw, &old_version);
+
+	if (new_version != old_version) {
+		PMD_DRV_LOG(INFO, "FW version is changed, new %u, old %u",
+				new_version, old_version);
+		*fw_changed = true;
+	} else {
+		PMD_DRV_LOG(INFO, "FW version is not changed and is %u", new_version);
+		*fw_changed = false;
+	}
+
+	return 0;
+}
+
 static int
 nfp_fw_reload(struct nfp_nsp *nsp,
 		char *fw_name)
@@ -1135,15 +1165,39 @@ nfp_fw_skip_load(const struct nfp_dev_info *dev_info,
 
 	return false;
 }
+static int
+nfp_fw_reload_for_single_pf(struct nfp_nsp *nsp,
+		char *fw_name,
+		struct nfp_cpp *cpp)
+{
+	int ret;
+	bool fw_changed = true;
+
+	if (nfp_nsp_fw_loaded(nsp)) {
+		ret = nfp_fw_check_change(cpp, fw_name, &fw_changed);
+		if (ret != 0)
+			return ret;
+	}
+
+	if (!fw_changed)
+		return 0;
+
+	ret = nfp_fw_reload(nsp, fw_name);
+	if (ret != 0)
+		return ret;
+
+	return 0;
+}
 
 static int
-nfp_fw_reload_for_multipf(struct nfp_nsp *nsp,
+nfp_fw_reload_for_multi_pf(struct nfp_nsp *nsp,
 		char *fw_name,
 		struct nfp_cpp *cpp,
 		const struct nfp_dev_info *dev_info,
 		struct nfp_multi_pf *multi_pf)
 {
 	int err;
+	bool fw_changed = true;
 	bool skip_load_fw = false;
 
 	err = nfp_net_keepalive_init(cpp, multi_pf);
@@ -1154,27 +1208,36 @@ nfp_fw_reload_for_multipf(struct nfp_nsp *nsp,
 
 	err = nfp_net_keepalive_start(multi_pf);
 	if (err != 0) {
-		nfp_net_keepalive_uninit(multi_pf);
 		PMD_DRV_LOG(ERR, "NFP write beat failed");
-		return err;
+		goto keepalive_uninit;
+	}
+
+	if (nfp_nsp_fw_loaded(nsp)) {
+		err = nfp_fw_check_change(cpp, fw_name, &fw_changed);
+		if (err != 0)
+			goto keepalive_stop;
 	}
 
-	if (nfp_nsp_fw_loaded(nsp))
+	if (!fw_changed)
 		skip_load_fw = nfp_fw_skip_load(dev_info, multi_pf);
 
 	if (skip_load_fw)
 		return 0;
 
 	err = nfp_fw_reload(nsp, fw_name);
-	if (err != 0) {
-		nfp_net_keepalive_stop(multi_pf);
-		nfp_net_keepalive_uninit(multi_pf);
-		return err;
-	}
+	if (err != 0)
+		goto keepalive_stop;
 
 	nfp_net_keepalive_clear_others(dev_info, multi_pf);
 
 	return 0;
+
+keepalive_stop:
+	nfp_net_keepalive_stop(multi_pf);
+keepalive_uninit:
+	nfp_net_keepalive_uninit(multi_pf);
+
+	return err;
 }
 
 static int
@@ -1231,9 +1294,9 @@ nfp_fw_setup(struct rte_pci_device *dev,
 	}
 
 	if (multi_pf->enabled)
-		err = nfp_fw_reload_for_multipf(nsp, fw_name, cpp, dev_info, multi_pf);
+		err = nfp_fw_reload_for_multi_pf(nsp, fw_name, cpp, dev_info, multi_pf);
 	else
-		err = nfp_fw_reload(nsp, fw_name);
+		err = nfp_fw_reload_for_single_pf(nsp, fw_name, cpp);
 
 	nfp_nsp_close(nsp);
 	return err;
diff --git a/drivers/net/nfp/nfp_net_common.c b/drivers/net/nfp/nfp_net_common.c
index 26ea4ec92f..20e628bfd1 100644
--- a/drivers/net/nfp/nfp_net_common.c
+++ b/drivers/net/nfp/nfp_net_common.c
@@ -2090,6 +2090,23 @@ nfp_net_get_nsp_info(struct nfp_net_hw *hw,
 	nfp_nsp_close(nsp);
 }
 
+void
+nfp_net_get_fw_version(struct nfp_net_hw *hw,
+		uint32_t *mip_version)
+{
+	struct nfp_mip *mip;
+
+	mip = nfp_mip_open(hw->cpp);
+	if (mip == NULL) {
+		*mip_version = 0;
+		return;
+	}
+
+	*mip_version = nfp_mip_fw_version(mip);
+
+	nfp_mip_close(mip);
+}
+
 static void
 nfp_net_get_mip_name(struct nfp_net_hw *hw,
 		char *mip_name)
diff --git a/drivers/net/nfp/nfp_net_common.h b/drivers/net/nfp/nfp_net_common.h
index 1c24045b7d..9c46bfd4f6 100644
--- a/drivers/net/nfp/nfp_net_common.h
+++ b/drivers/net/nfp/nfp_net_common.h
@@ -316,6 +316,8 @@ int nfp_net_fec_get(struct rte_eth_dev *dev,
 		uint32_t *fec_capa);
 int nfp_net_fec_set(struct rte_eth_dev *dev,
 		uint32_t fec_capa);
+void nfp_net_get_fw_version(struct nfp_net_hw *hw,
+		uint32_t *fw_version);
 
 #define NFP_PRIV_TO_APP_FW_NIC(app_fw_priv)\
 	((struct nfp_app_fw_nic *)app_fw_priv)
-- 
2.39.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 3/3] net/nfp: add force reload firmware option
  2024-03-01  8:42 ` [PATCH v2 0/3] reload the firmware as needed Chaoyong He
  2024-03-01  8:42   ` [PATCH v2 1/3] net/nfp: add the elf module Chaoyong He
  2024-03-01  8:42   ` [PATCH v2 2/3] net/nfp: reload the firmware only when firmware changed Chaoyong He
@ 2024-03-01  8:42   ` Chaoyong He
  2024-03-04 13:28   ` [PATCH v2 0/3] reload the firmware as needed Ferruh Yigit
  3 siblings, 0 replies; 17+ messages in thread
From: Chaoyong He @ 2024-03-01  8:42 UTC (permalink / raw)
  To: dev; +Cc: oss-drivers, Peng Zhang, Chaoyong He

From: Peng Zhang <peng.zhang@corigine.com>

Add an option to force reload the firmware.

This option can be ignored in some case, for example:
When using a 2 port NFP card and both with this reload firmware option,
only the first one will cause the firmware reload and the second one
will be ignored.

Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
---
 doc/guides/nics/nfp.rst          |  21 +++++++
 drivers/net/nfp/nfp_ethdev.c     | 104 +++++++++++++++++++++++++++----
 drivers/net/nfp/nfp_net_common.h |   8 +++
 3 files changed, 121 insertions(+), 12 deletions(-)

diff --git a/doc/guides/nics/nfp.rst b/doc/guides/nics/nfp.rst
index fee1860f4a..4f2be0856c 100644
--- a/doc/guides/nics/nfp.rst
+++ b/doc/guides/nics/nfp.rst
@@ -225,6 +225,27 @@ The ctrl vNIC service handles various control messages, for example, the
 creation and configuration of representor port, the pattern and action of flow
 rules, the statistics of flow rules, etc.
 
+NFP devargs
+-----------
+
+- ``force_reload_fw`` (default **0**)
+
+   The NFP PF PMD supports force reload the firmware and ignore the firmware
+   version. For example, user can force a PF with PCI ID 0000:af:00.0 reload
+   firmware by:
+
+   .. code-block:: console
+
+      -a af:00.0,force_reload_fw=1 -- -i
+
+   .. Note::
+
+      This parameter can be ignored in some case.
+
+      For example: when using a 2 port NFP card and both with this reload
+      firmware option, only the first one will cause the firmware reload and
+      the second one will be ignored.
+
 Metadata Format
 ---------------
 
diff --git a/drivers/net/nfp/nfp_ethdev.c b/drivers/net/nfp/nfp_ethdev.c
index 902a62a86b..8c38b4e134 100644
--- a/drivers/net/nfp/nfp_ethdev.c
+++ b/drivers/net/nfp/nfp_ethdev.c
@@ -9,6 +9,7 @@
 
 #include <eal_firmware.h>
 #include <rte_alarm.h>
+#include <rte_kvargs.h>
 
 #include "flower/nfp_flower.h"
 #include "nfd3/nfp_nfd3.h"
@@ -31,6 +32,71 @@
 #define NFP_NET_APP_CAP_SP_INDIFF       RTE_BIT64(0) /* Indifferent to port speed */
 
 #define NFP_PF_DRIVER_NAME net_nfp_pf
+#define NFP_PF_FORCE_RELOAD_FW   "force_reload_fw"
+
+static int
+nfp_devarg_handle_int(const char *key,
+		const char *value,
+		void *extra_args)
+{
+	char *end_ptr;
+	uint64_t *num = extra_args;
+
+	if (value == NULL)
+		return -EPERM;
+
+	*num = strtoul(value, &end_ptr, 10);
+	if (*num == ULONG_MAX) {
+		PMD_DRV_LOG(ERR, "%s: '%s' is not a valid param", key, value);
+		return -ERANGE;
+	} else if (value == end_ptr) {
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+static void
+nfp_devarg_parse_force_reload_fw(struct rte_kvargs *kvlist,
+		bool *force_reload_fw)
+{
+	int ret;
+	uint64_t value;
+
+
+	if (rte_kvargs_count(kvlist, NFP_PF_FORCE_RELOAD_FW) != 1)
+		return;
+
+	ret = rte_kvargs_process(kvlist, NFP_PF_FORCE_RELOAD_FW, &nfp_devarg_handle_int, &value);
+	if (ret != 0)
+		return;
+
+	if (value == 1)
+		*force_reload_fw = true;
+	else if (value == 0)
+		*force_reload_fw = false;
+	else
+		PMD_DRV_LOG(ERR, "The param does not work, the format is %s=0/1",
+				NFP_PF_FORCE_RELOAD_FW);
+}
+
+static void
+nfp_devargs_parse(struct nfp_devargs *nfp_devargs_param,
+		const struct rte_devargs *devargs)
+{
+	struct rte_kvargs *kvlist;
+
+	if (devargs == NULL)
+		return;
+
+	kvlist = rte_kvargs_parse(devargs->args, NULL);
+	if (kvlist == NULL)
+		return;
+
+	nfp_devarg_parse_force_reload_fw(kvlist, &nfp_devargs_param->force_reload_fw);
+
+	rte_kvargs_free(kvlist);
+}
 
 static void
 nfp_net_pf_read_mac(struct nfp_app_fw_nic *app_fw_nic,
@@ -1116,7 +1182,8 @@ nfp_fw_reload(struct nfp_nsp *nsp,
 
 static bool
 nfp_fw_skip_load(const struct nfp_dev_info *dev_info,
-		struct nfp_multi_pf *multi_pf)
+		struct nfp_multi_pf *multi_pf,
+		bool *reload_fw)
 {
 	uint8_t i;
 	uint64_t tmp_beat;
@@ -1150,6 +1217,11 @@ nfp_fw_skip_load(const struct nfp_dev_info *dev_info,
 				in_use++;
 				abnormal--;
 				beat[port_num] = 0;
+				if (*reload_fw) {
+					*reload_fw = false;
+					PMD_DRV_LOG(ERR, "The param %s does not work",
+							NFP_PF_FORCE_RELOAD_FW);
+				}
 			}
 		}
 
@@ -1168,12 +1240,13 @@ nfp_fw_skip_load(const struct nfp_dev_info *dev_info,
 static int
 nfp_fw_reload_for_single_pf(struct nfp_nsp *nsp,
 		char *fw_name,
-		struct nfp_cpp *cpp)
+		struct nfp_cpp *cpp,
+		bool force_reload_fw)
 {
 	int ret;
 	bool fw_changed = true;
 
-	if (nfp_nsp_fw_loaded(nsp)) {
+	if (nfp_nsp_fw_loaded(nsp) && !force_reload_fw) {
 		ret = nfp_fw_check_change(cpp, fw_name, &fw_changed);
 		if (ret != 0)
 			return ret;
@@ -1194,11 +1267,13 @@ nfp_fw_reload_for_multi_pf(struct nfp_nsp *nsp,
 		char *fw_name,
 		struct nfp_cpp *cpp,
 		const struct nfp_dev_info *dev_info,
-		struct nfp_multi_pf *multi_pf)
+		struct nfp_multi_pf *multi_pf,
+		bool force_reload_fw)
 {
 	int err;
 	bool fw_changed = true;
 	bool skip_load_fw = false;
+	bool reload_fw = force_reload_fw;
 
 	err = nfp_net_keepalive_init(cpp, multi_pf);
 	if (err != 0) {
@@ -1212,16 +1287,16 @@ nfp_fw_reload_for_multi_pf(struct nfp_nsp *nsp,
 		goto keepalive_uninit;
 	}
 
-	if (nfp_nsp_fw_loaded(nsp)) {
+	if (nfp_nsp_fw_loaded(nsp) && !reload_fw) {
 		err = nfp_fw_check_change(cpp, fw_name, &fw_changed);
 		if (err != 0)
 			goto keepalive_stop;
 	}
 
-	if (!fw_changed)
-		skip_load_fw = nfp_fw_skip_load(dev_info, multi_pf);
+	if (!fw_changed || reload_fw)
+		skip_load_fw = nfp_fw_skip_load(dev_info, multi_pf, &reload_fw);
 
-	if (skip_load_fw)
+	if (skip_load_fw && !reload_fw)
 		return 0;
 
 	err = nfp_fw_reload(nsp, fw_name);
@@ -1246,7 +1321,8 @@ nfp_fw_setup(struct rte_pci_device *dev,
 		struct nfp_eth_table *nfp_eth_table,
 		struct nfp_hwinfo *hwinfo,
 		const struct nfp_dev_info *dev_info,
-		struct nfp_multi_pf *multi_pf)
+		struct nfp_multi_pf *multi_pf,
+		bool force_reload_fw)
 {
 	int err;
 	char fw_name[125];
@@ -1294,9 +1370,10 @@ nfp_fw_setup(struct rte_pci_device *dev,
 	}
 
 	if (multi_pf->enabled)
-		err = nfp_fw_reload_for_multi_pf(nsp, fw_name, cpp, dev_info, multi_pf);
+		err = nfp_fw_reload_for_multi_pf(nsp, fw_name, cpp, dev_info, multi_pf,
+				force_reload_fw);
 	else
-		err = nfp_fw_reload_for_single_pf(nsp, fw_name, cpp);
+		err = nfp_fw_reload_for_single_pf(nsp, fw_name, cpp, force_reload_fw);
 
 	nfp_nsp_close(nsp);
 	return err;
@@ -1769,8 +1846,10 @@ nfp_pf_init(struct rte_pci_device *pci_dev)
 		nfp_eth_set_configured(cpp, index, 0);
 	}
 
+	nfp_devargs_parse(&pf_dev->devargs, pci_dev->device.devargs);
+
 	if (nfp_fw_setup(pci_dev, cpp, nfp_eth_table, hwinfo,
-			dev_info, &pf_dev->multi_pf) != 0) {
+			dev_info, &pf_dev->multi_pf, pf_dev->devargs.force_reload_fw) != 0) {
 		PMD_INIT_LOG(ERR, "Error when uploading firmware");
 		ret = -EIO;
 		goto eth_table_cleanup;
@@ -2151,3 +2230,4 @@ static struct rte_pci_driver rte_nfp_net_pf_pmd = {
 RTE_PMD_REGISTER_PCI(NFP_PF_DRIVER_NAME, rte_nfp_net_pf_pmd);
 RTE_PMD_REGISTER_PCI_TABLE(NFP_PF_DRIVER_NAME, pci_id_nfp_pf_net_map);
 RTE_PMD_REGISTER_KMOD_DEP(NFP_PF_DRIVER_NAME, "* igb_uio | uio_pci_generic | vfio");
+RTE_PMD_REGISTER_PARAM_STRING(NFP_PF_DRIVER_NAME, NFP_PF_FORCE_RELOAD_FW "=<0|1>");
diff --git a/drivers/net/nfp/nfp_net_common.h b/drivers/net/nfp/nfp_net_common.h
index 9c46bfd4f6..628c0d3491 100644
--- a/drivers/net/nfp/nfp_net_common.h
+++ b/drivers/net/nfp/nfp_net_common.h
@@ -94,6 +94,11 @@ struct nfp_process_share {
 	struct nfp_flower_service *fl_service;
 };
 
+struct nfp_devargs {
+	/** Force reload firmware */
+	bool force_reload_fw;
+};
+
 struct nfp_pf_dev {
 	/** Backpointer to associated pci device */
 	struct rte_pci_device *pci_dev;
@@ -129,6 +134,9 @@ struct nfp_pf_dev {
 	/** Synchronized info */
 	struct nfp_sync *sync;
 	struct nfp_process_share process_share;
+
+	/** NFP devarg param */
+	struct nfp_devargs devargs;
 };
 
 #define NFP_NET_FLOW_LIMIT    1024
-- 
2.39.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/3] net/nfp: add the elf module
  2024-02-28 22:18   ` Stephen Hemminger
@ 2024-03-01 17:22     ` Ferruh Yigit
  2024-03-04  1:13       ` Chaoyong He
  0 siblings, 1 reply; 17+ messages in thread
From: Ferruh Yigit @ 2024-03-01 17:22 UTC (permalink / raw)
  To: Stephen Hemminger, Chaoyong He; +Cc: dev, oss-drivers, Peng Zhang, Long Wu

On 2/28/2024 10:18 PM, Stephen Hemminger wrote:
> On Tue, 27 Feb 2024 19:15:49 +0800
> Chaoyong He <chaoyong.he@corigine.com> wrote:
> 
>> From: Peng Zhang <peng.zhang@corigine.com>
>>
>> Add the elf module, which can get mip information from the
>> firmware ELF file.
>>
>> Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
>> Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
>> Reviewed-by: Long Wu <long.wu@corigine.com>
>> ---
> 
> Why are you rolling your own ELF parser?
> There are libraries to do this such as libelf.
> Libelf is already used in the BPF part of DPDK.
>

There cons and pros to depend external library, as this is in the
limited scope of the driver I am less concerned about local code.

Chaoyong, what is your take on the issue, did you consider using libelf
library option?


^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH 1/3] net/nfp: add the elf module
  2024-03-01 17:22     ` Ferruh Yigit
@ 2024-03-04  1:13       ` Chaoyong He
  2024-03-04  8:50         ` Ferruh Yigit
  0 siblings, 1 reply; 17+ messages in thread
From: Chaoyong He @ 2024-03-04  1:13 UTC (permalink / raw)
  To: Ferruh Yigit, Stephen Hemminger; +Cc: dev, oss-drivers, Nole Zhang, Long Wu

> On 2/28/2024 10:18 PM, Stephen Hemminger wrote:
> > On Tue, 27 Feb 2024 19:15:49 +0800
> > Chaoyong He <chaoyong.he@corigine.com> wrote:
> >
> >> From: Peng Zhang <peng.zhang@corigine.com>
> >>
> >> Add the elf module, which can get mip information from the firmware
> >> ELF file.
> >>
> >> Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
> >> Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
> >> Reviewed-by: Long Wu <long.wu@corigine.com>
> >> ---
> >
> > Why are you rolling your own ELF parser?
> > There are libraries to do this such as libelf.
> > Libelf is already used in the BPF part of DPDK.
> >
> 
> There cons and pros to depend external library, as this is in the limited scope of
> the driver I am less concerned about local code.
> 
> Chaoyong, what is your take on the issue, did you consider using libelf library
> option?

Firstly, the nffw firmware file is a customed ELF file, we are not sure the libelf library can meet our needs totally.
Then, we share the same logic with our BSP code, and we don't want to have two different logic for the same requirement.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/3] net/nfp: add the elf module
  2024-03-04  1:13       ` Chaoyong He
@ 2024-03-04  8:50         ` Ferruh Yigit
  0 siblings, 0 replies; 17+ messages in thread
From: Ferruh Yigit @ 2024-03-04  8:50 UTC (permalink / raw)
  To: Chaoyong He, Stephen Hemminger; +Cc: dev, oss-drivers, Nole Zhang, Long Wu

On 3/4/2024 1:13 AM, Chaoyong He wrote:
>> On 2/28/2024 10:18 PM, Stephen Hemminger wrote:
>>> On Tue, 27 Feb 2024 19:15:49 +0800
>>> Chaoyong He <chaoyong.he@corigine.com> wrote:
>>>
>>>> From: Peng Zhang <peng.zhang@corigine.com>
>>>>
>>>> Add the elf module, which can get mip information from the firmware
>>>> ELF file.
>>>>
>>>> Signed-off-by: Peng Zhang <peng.zhang@corigine.com>
>>>> Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
>>>> Reviewed-by: Long Wu <long.wu@corigine.com>
>>>> ---
>>>
>>> Why are you rolling your own ELF parser?
>>> There are libraries to do this such as libelf.
>>> Libelf is already used in the BPF part of DPDK.
>>>
>>
>> There cons and pros to depend external library, as this is in the limited scope of
>> the driver I am less concerned about local code.
>>
>> Chaoyong, what is your take on the issue, did you consider using libelf library
>> option?
> 
> Firstly, the nffw firmware file is a customed ELF file, we are not sure the libelf library can meet our needs totally.
> Then, we share the same logic with our BSP code, and we don't want to have two different logic for the same requirement.
>

Looks reasonable to me, thanks for clarification.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 0/3] reload the firmware as needed
  2024-03-01  8:42 ` [PATCH v2 0/3] reload the firmware as needed Chaoyong He
                     ` (2 preceding siblings ...)
  2024-03-01  8:42   ` [PATCH v2 3/3] net/nfp: add force reload firmware option Chaoyong He
@ 2024-03-04 13:28   ` Ferruh Yigit
  3 siblings, 0 replies; 17+ messages in thread
From: Ferruh Yigit @ 2024-03-04 13:28 UTC (permalink / raw)
  To: Chaoyong He, dev; +Cc: oss-drivers

On 3/1/2024 8:42 AM, Chaoyong He wrote:
> Add the necessary logic to get firmware version from firmware file, and
> only reload the firmware when the firmware version changed.
> 
> Also add a device argument which can force reload the firmware and
> ignore the firmware version.
> 
> ---
> v2:
> * Update commit log to explain what 'MIP' is.
> * Document about the new add 'force_reload_fw' device argument.
> ---
> 
> Peng Zhang (3):
>   net/nfp: add the elf module
>   net/nfp: reload the firmware only when firmware changed
>   net/nfp: add force reload firmware option
>

Series applied to dpdk-next-net/main, thanks.

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2024-03-04 13:29 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-27 11:15 [PATCH 0/3] reload the firmware as needed Chaoyong He
2024-02-27 11:15 ` [PATCH 1/3] net/nfp: add the elf module Chaoyong He
2024-02-28 16:50   ` Ferruh Yigit
2024-03-01  2:38     ` Chaoyong He
2024-02-28 22:18   ` Stephen Hemminger
2024-03-01 17:22     ` Ferruh Yigit
2024-03-04  1:13       ` Chaoyong He
2024-03-04  8:50         ` Ferruh Yigit
2024-02-27 11:15 ` [PATCH 2/3] net/nfp: reload the firmware only when firmware changed Chaoyong He
2024-02-27 11:15 ` [PATCH 3/3] net/nfp: add force reload firmware option Chaoyong He
2024-02-28 16:53   ` Ferruh Yigit
2024-03-01  2:06     ` Chaoyong He
2024-03-01  8:42 ` [PATCH v2 0/3] reload the firmware as needed Chaoyong He
2024-03-01  8:42   ` [PATCH v2 1/3] net/nfp: add the elf module Chaoyong He
2024-03-01  8:42   ` [PATCH v2 2/3] net/nfp: reload the firmware only when firmware changed Chaoyong He
2024-03-01  8:42   ` [PATCH v2 3/3] net/nfp: add force reload firmware option Chaoyong He
2024-03-04 13:28   ` [PATCH v2 0/3] reload the firmware as needed Ferruh Yigit

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).