From: Chaoyong He <chaoyong.he@corigine.com>
To: dev@dpdk.org
Cc: oss-drivers@corigine.com, Peng Zhang <peng.zhang@corigine.com>,
Chaoyong He <chaoyong.he@corigine.com>,
Long Wu <long.wu@corigine.com>
Subject: [PATCH 1/3] net/nfp: add the elf module
Date: Tue, 27 Feb 2024 19:15:49 +0800 [thread overview]
Message-ID: <20240227111551.3773862-2-chaoyong.he@corigine.com> (raw)
In-Reply-To: <20240227111551.3773862-1-chaoyong.he@corigine.com>
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
next prev parent reply other threads:[~2024-02-27 11:16 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-02-27 11:15 [PATCH 0/3] reload the firmware as needed Chaoyong He
2024-02-27 11:15 ` Chaoyong He [this message]
2024-02-28 16:50 ` [PATCH 1/3] net/nfp: add the elf module 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240227111551.3773862-2-chaoyong.he@corigine.com \
--to=chaoyong.he@corigine.com \
--cc=dev@dpdk.org \
--cc=long.wu@corigine.com \
--cc=oss-drivers@corigine.com \
--cc=peng.zhang@corigine.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).