From patchwork Fri Mar 1 08:42:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chaoyong He X-Patchwork-Id: 137655 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id F201443BC0; Fri, 1 Mar 2024 09:43:15 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 0561643320; Fri, 1 Mar 2024 09:43:10 +0100 (CET) Received: from NAM11-CO1-obe.outbound.protection.outlook.com (mail-co1nam11on2134.outbound.protection.outlook.com [40.107.220.134]) by mails.dpdk.org (Postfix) with ESMTP id 71568400D5 for ; Fri, 1 Mar 2024 09:43:07 +0100 (CET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=eTqQM+Gk+ZY966iWCFuu8jRNWsGCSEuYXdkHHwkQQ4HoqraKBExTkQeJkosq596XD6et/CMybn71YpybuqFCn9m/Ulizr10TaGTfeR5ZV+r6fY9fMcikB6oK/wyQkuHuqCaxvuWos6Fh8Bqkc2Myr9qf8goOWX6df8hAp1QtZrBZhf4mbYmNgoad/GSg9anN0aMufJUOkOG+rCEslZX1jAnVoUGg7QbdxSZ2m6VQOexVmtZMlD/M1CbPlI/fvbraNsh5tVP8VDyVVukLsr54ZrJ501vaGa54DOiisRiLLhEiJatCH54F+H/MPkEujRpX7087sJ9ZLZH8Q21s3gjc5Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=JJVWxYvpEmsKXer3gmq/e7+MiPU/4lmmC3fd/RDyqVA=; b=Zy0qglsAMJlWazB9rrpSZyAp3Xtd7YMj/O5TbUJG1SlOrHXkgLqp3XjRJ1J/Lf6alTP56In8IILVMTjEg21/HkzuRv7w2i4I5c+vZPAl2cxiJZvgodkiR+3Cl9FFFMbEYrFubm0OlURUZ6YVm3bCK42Gnm+uNLZc5pbaRPG/GKZ01xqC8un1S2eDzXoPCCudJ0hhYvFS15bC1pHJ1kfyhIPFBJNp3kEKGy0Fh2sueJeclqooKxKXDK/euxt6mss8XHTwIkQ9i8W/W1EkwxCrIy6v3ttGoE6BLgkWMVn5cTntNsocuvpT8bQQhX15azB7QSCGIGeJ8U7VzSJR3PMc7g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=corigine.com; dmarc=pass action=none header.from=corigine.com; dkim=pass header.d=corigine.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=corigine.onmicrosoft.com; s=selector2-corigine-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=JJVWxYvpEmsKXer3gmq/e7+MiPU/4lmmC3fd/RDyqVA=; b=ODFQXnH50GsaMukigkcxHQ+sQO9qcGLDrqF9AJjalCEa1QCWInQrrsuznv4t8IS2ntfO3TnIVtshWo4jGyardYnSOk7noHLKX2UFg8vEPJashvGl0+cYs7rIxBI2l2PMXJ/PJP6J1XaALHJ2WSohPGdI8Hj6Pu1WlzP79/8jjXg= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=corigine.com; Received: from SJ0PR13MB5545.namprd13.prod.outlook.com (2603:10b6:a03:424::5) by MW3PR13MB4153.namprd13.prod.outlook.com (2603:10b6:303:5c::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7316.41; Fri, 1 Mar 2024 08:43:05 +0000 Received: from SJ0PR13MB5545.namprd13.prod.outlook.com ([fe80::ec12:7411:559a:850e]) by SJ0PR13MB5545.namprd13.prod.outlook.com ([fe80::ec12:7411:559a:850e%4]) with mapi id 15.20.7316.039; Fri, 1 Mar 2024 08:43:05 +0000 From: Chaoyong He To: dev@dpdk.org Cc: oss-drivers@corigine.com, Peng Zhang , Chaoyong He , Long Wu Subject: [PATCH v2 1/3] net/nfp: add the elf module Date: Fri, 1 Mar 2024 16:42:42 +0800 Message-Id: <20240301084244.190484-2-chaoyong.he@corigine.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240301084244.190484-1-chaoyong.he@corigine.com> References: <20240227111551.3773862-1-chaoyong.he@corigine.com> <20240301084244.190484-1-chaoyong.he@corigine.com> X-ClientProxiedBy: SJ0PR03CA0206.namprd03.prod.outlook.com (2603:10b6:a03:2ef::31) To SJ0PR13MB5545.namprd13.prod.outlook.com (2603:10b6:a03:424::5) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SJ0PR13MB5545:EE_|MW3PR13MB4153:EE_ X-MS-Office365-Filtering-Correlation-Id: 37549033-bbfc-4be8-a805-08dc39cb9caa X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 5/0GBNt/L9+osPtsZi27n2QeR0gVgj6dhbsQp1LtCCZYp9KRD8AkYbxCm0iS2yu3BWbAulNlGHRd12BEMQmFvRX3nWGykAdZ0z0zcSuxo+sS51gQlmW8dy/PbyCi9GdrRShKdfkrthpQAtzpplS3JLoUo+i10ZW6qgTBPwwaLexvGQY0Owzn+stLw8guXXRwvYUf8fPBexoaYwIwy9szvuyvq7PQyYo+5mkkOrf6I3RC41dPnRwQoMun1cWdQEC02pPbFwnO1cSSaHkKUjJGvhf2F+VWdTOkVz0fvzLcKO2l/jNYED+dhBPscph0MpCietcFJHhAQ989crLuVkty57HSrv/O1Qh4Jj/Y2YWQxYUhX6dXc4RocL5Pyc1YkCXvw4TzxZrkRlSl3iMD2miHT3x9qOLO6gRXP92G4EJ09wgXDgqyHOpOtTKdsFQtqAwBiT/7MGYZH+yNt2s2YNWvz9/EaRZy+iGrlftvUHtwUevVkHU+u73KDXFiXU7SWVjzKL5SlIG7ZUvcBWGFsKH2KUMqyT36UviUZYZEIBPb1/tMHaJw+sD+p+1GuzKCmQwdl5uty8/nVy728U/iLOFBrqLYK7bZ99mtS19RnLr7XjOD5DWQCN/16xPUndNbgbbLYwzM7J++DMUkRydvxQNEZmtrsdLZJFctJOQkkUb9S1BkxCjVcTNcIBoxiIvitlOJtSuRLpgvCExnputs0+RxuA== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:SJ0PR13MB5545.namprd13.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230031)(38350700005); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: sR0wrXNx1QsWjsTIm8Cc7+excQzUv93KXL9HLct8mOmSSx+uIJILnWLG0QDi5pbzv57LwJzFxEASdJfmJq2l/cllVInat5dAQ7KJ812hy8u1XTrH/YSwYOM2zp1cvaLKwQ1ZarRPo4kX+ledLxZMrYjciFJS5z13nj+R//zOIEoHQTjXgWIst5CbDATIEnXWPueLy3DmjU+zM79Maton8U8Tyari4PvmkmiQY8auEFyt26yCdExHZt1tQ9zD8LGq8rRwSmCwU1YkHQPXCVI0K2c53RtRNBwhObOWa4zk6czE4+g91BqYAY1jF103N9EJRfRWi/BU+vqxERWyYMeuUdOZ4i8AHDNEZQBWjz59vDPkN+lQVMGWAoiMfbz5sSB8UnBXJ1WAgwskLCyuiZBFy+NiL/P0fpocj4Y/8UVY2MB1nJr6aFSWrqXBzHZDti2j0uOAFMYbZbDueSBZjekM0idY3DbFVAWzGfp4dOuNpWGiqd5tmyp1bKU45Hu3/EtTnNWpQJVXN16I5fajjpJq+hrdDsZk5eHb+0R6+lwOaV/VCvm1ZeWdC91OOO47iZ9mbYWVFAAe8IOu24GMfieRHsn9BKvG/Zw0fGBwWuQtKAt/VJcfyZf9tkvNZQPzEFpXRf5Qgmy+nWnBE/kMsvBxc9ZiJfncJpcGMv7LZKgEBETGwxsEYGH/kUG8HyLKZmPyljh5IX/g9TQKaSPgkVyiPeQ7wWWp8Oqq6udPcc5eHw2Z0f9UNDmcZKOAlbXAmyYxArmh5VRsbGpUFGvZRVgJ+K1VXCex1i9q6q8OdzBxvm99s3IEbUES7/EZTv3ZCoBfz/gzA0Ml/PkdcKDJ4Sf+zG7xkLaHwy3kWYjadFz/dSlTaBk617emoE9HbRysVdUQtezlyhGp/GFu3PTr8o0CWGJyAIHJyhZoF4GGNc5rrPTPZWj4+R38SGDigNK06ODBkvYe/Pkh0loPucKofY2VdHRQIvqr0VZjXCekgYhhV0D2bAt3Z5L1HRSYB2nMvxgT0/cDSdNjJmAUaiLNWBUK1tk2MSfivsmcuL1vEHhd4lyXI/F3Mvnn8GMVMbEKeyEmcjkQ9pdTg34M4DnxRpzIoc1hQuNMXz1iolrVLIhqtuDTb3zD3VBNfjvjPo4xSqC3XWPw05szWty+u1Pcuqbpc60Fn0ip3m4xNKDFdXa3L53+RjFaO1T2GqoqULTqb7THECm3Gw+VW/dWFpvOMW4G7wkJEtWnQ92DdDuheZ0Yxvfecqyid6V5NTaooqsQDFK2mTcAQfWOJo/EnjyZAusv2RZPPqNtcxdbEwVb76S6VUAjkqCeLYyFegJn5tcHIkJrDx5LG+pOcHbe5ahyH9+5ppP/0P08RrypTF+y66M8zt5zq7KUx5+UQyf62RMZ5SewnLKT/JC83RMetvCDXtNGQtKNp5gCRifPrbexb7HRL51rwRZbIRSqUYlYggXjnkHqiE28eHp7wTa4B/3rvUqzNwtTCH97QbvP2xGHxhiwLEVlsi8JYyhowzNQZDknHT5TBm9rwniNYt6jwbLDFP5W19gl8zI7UdIVD3M4p5G1SAD/cf87+RHihjrnFlUV4IWzfbhilv7UqAqbBG2+Gotb9A== X-OriginatorOrg: corigine.com X-MS-Exchange-CrossTenant-Network-Message-Id: 37549033-bbfc-4be8-a805-08dc39cb9caa X-MS-Exchange-CrossTenant-AuthSource: SJ0PR13MB5545.namprd13.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 01 Mar 2024 08:43:05.6026 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: fe128f2c-073b-4c20-818e-7246a585940c X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: z4icKF1xqaiBl979hEF+UOSiTwOW4eA/DbR+fpDvEmAtDsHemGwwH3iJQjnx+hQ4Y/Gt7rHsVbeF5ajZvc0749qkAig+2G6sRPao9GwUmEU= X-MS-Exchange-Transport-CrossTenantHeadersStamped: MW3PR13MB4153 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org From: Peng Zhang Add the 'nfp_elf' module, which can get new MIP(Microcode Information Page) information from the firmware ELF file. Signed-off-by: Peng Zhang Reviewed-by: Chaoyong He Reviewed-by: Long Wu --- 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 +#include +#include + +#include +#include +#include + +#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 + +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);