From patchwork Mon Jan 15 02:54:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chaoyong He X-Patchwork-Id: 135868 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 31E75438C7; Mon, 15 Jan 2024 03:55:36 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 73903406BC; Mon, 15 Jan 2024 03:55:01 +0100 (CET) Received: from NAM11-CO1-obe.outbound.protection.outlook.com (mail-co1nam11on2129.outbound.protection.outlook.com [40.107.220.129]) by mails.dpdk.org (Postfix) with ESMTP id B39B640689 for ; Mon, 15 Jan 2024 03:54:58 +0100 (CET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=ZkqPA+oob2Un61YNmqGpPY+nvLZKNew48m748MA1L3aicYd+nvVmt49KHpYnwWmv0JNYatMDRyo7urCbFEg2N20W8YtUrBh1QWu2z3rfWWKrwqwn71EZUVv61u1eHjB70R76cuxqCru8xjznQU3GbdRy35oKw27yagjfOq8JoVpeSwLw6A8lp2WbLqtHsf+sYkekFpfAWyW4ZY/fO/eNqNYlv9eu5qbafVwAzxR9kY0cFpZqarTOYT5wTCACJBNfLgvGdqboCtD/O6XpbFVdiO3dca2+plaaw1N70doFd4IisAYfGpdHhZZu//HeK2KLFDVTTtS+itIJVRWp2Ziayg== 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=2yEpmrAYOyS5RCf6IMbqKx8wCFEia9wj94hth+ftf3A=; b=U/5fqLN9r5Jg2MnUKIZxnRcno5ljL0VbvR+6neENJE5RXDxg6MC9lCv6wBxIBAmfgQHUkaZzJvXSB7LNkkPCjPWsnCo2klpH1EDj5X6Ccd8w88/YYPKFOHE3/IIQ3LwUR6eUC9FJhh6pRkfT4aygnYgE1rO5xpjx07VZbySDbWP3CFFJ/GQ6vOVqc6dVDUH8g4VgFjJvRU8tCakQBUT9RoSRPwUXfzwqcCDLNvZtwyOZJiCqXp+1PKt/NDewXNirwc/RxrYWL1idXqNG5nyWmiixouKyNV8gsL/RdHzwvi15L1O0RqvicAIa+daZMNDjfL2ToMYymELjnjEmkYSjlA== 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=2yEpmrAYOyS5RCf6IMbqKx8wCFEia9wj94hth+ftf3A=; b=pBiKFQIQsCtS514RsOvYwCWerNUCXTakYK0PPhpJclX3TC4BK1b0CoHv/VUU7XfsQN10XEZYIyRqIupky69AmkxD+QNtigrGMvXqItuMpajWe5SwFWJd4JPF3mQ5GlDtfodZf63M82hl/9kPO7zq1Tp9ih/dSfRog0hvvSAEN/A= 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 BLAPR13MB4578.namprd13.prod.outlook.com (2603:10b6:208:332::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7181.26; Mon, 15 Jan 2024 02:54:56 +0000 Received: from SJ0PR13MB5545.namprd13.prod.outlook.com ([fe80::8e02:f738:570a:f8aa]) by SJ0PR13MB5545.namprd13.prod.outlook.com ([fe80::8e02:f738:570a:f8aa%7]) with mapi id 15.20.7181.026; Mon, 15 Jan 2024 02:54:56 +0000 From: Chaoyong He To: dev@dpdk.org Cc: oss-drivers@corigine.com, Peng Zhang , Chaoyong He , Long Wu Subject: [PATCH 6/8] net/nfp: add the elf module Date: Mon, 15 Jan 2024 10:54:17 +0800 Message-Id: <20240115025419.2447759-7-chaoyong.he@corigine.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20240115025419.2447759-1-chaoyong.he@corigine.com> References: <20240115025419.2447759-1-chaoyong.he@corigine.com> X-ClientProxiedBy: PH0PR07CA0025.namprd07.prod.outlook.com (2603:10b6:510:5::30) To SJ0PR13MB5545.namprd13.prod.outlook.com (2603:10b6:a03:424::5) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SJ0PR13MB5545:EE_|BLAPR13MB4578:EE_ X-MS-Office365-Filtering-Correlation-Id: 415d87fa-5800-49ff-c666-08dc15755a8d X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: IerH3q8M31HdcemrgK7jU5+0kemHQvpZ89aiYmsVO0KWAtlokVwoiWIZbCKjVuhm6TNfPXNXhj4oXh+O3OSTmI41uXvyM2IP6obgcZ5wtQKzUfHsK3YNwjvN1/fgmALuJsvhe9eja+ARlinuLXNudreQTazrnReYM7XEu4R4xfpl/mVZ1eVyEyhrG3KD2X3HZE+vOvJawJpVE6SGT8LzRmmXg5HA8fyAGuIxFHGtlBuZz6UEpSZGyk5m4zk0khTMqO9Sj9DQS5bn3BJi7OhgG0f2x/BiPKexjVWSDtlLhirQMmhRjVYsBiEKz7bE7FgYqWnC6u8qnGX+2BciCl5IMkcl9LygGTPLmpxYGPkj9PWqD9T/UCEIoMsfTaHMsdm1enNXkt8pYyV+KhrssFhAosMYSVwapvuI1RNMZDQ/jFXSg0z6O0Za8YO4ZZdxv2vd4PllkVJVglNY6PBFipfa2DqQcrjaj9+0sOWdOS5hTqdsrrL0cs7NAIL6g0Xjzx38/Av29WXej1oEtI1296Mguxy+1rY6o+Fzw1gnI+qLqrn/gdvxR1nZVFY+i/XCtAwc7y8qKfYjH8mPi/+UVTd1HIk64czeqnprJ/iwskrxR8ahjQnbwV/GLmS8zOlnW8Ir 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)(376002)(396003)(346002)(366004)(136003)(39830400003)(230922051799003)(1800799012)(451199024)(64100799003)(186009)(83380400001)(107886003)(52116002)(36756003)(86362001)(38350700005)(6512007)(6666004)(8676002)(30864003)(8936002)(4326008)(44832011)(5660300002)(2616005)(1076003)(26005)(38100700002)(66946007)(6916009)(316002)(66476007)(54906003)(66556008)(2906002)(41300700001)(6486002)(6506007)(478600001); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: +9sP2K4xHa1i7mi0ZBAkC7lO9pCcdu06ninuopIJamm6TeHv+JFnlytghJUC3OuDujrYGQSba2StMpjriyjKHyxWc2iQgR2LWv++GsoBWsG1/UlaFsBPIiwS3LUu/bWXbUHKkTAwH6S2EibGv0OOjTYD8rhOPK3qudCK5s6NJoIpLMJrevBhxzjpAaIeweWxrsYiXILR0/rXPS0K2nn6HZCnkNrkX9Eu0zd3mLTx3H9Zb93LKrPK72RVNWYqbw6bcw+MSrCefGZYJtDRZgj27ZQITUF0/syn95Lh6SlXFHFK+mp8C+2iFtVDZTbiuQkjTNkHx4mYS9wgs7u4xZUEsTBLAJlmvuoIJO3Q25qevAoagBFQrDg2r536tPrWTjcq+3bjedmI2iBoSDPmIviSXyz4S+OlN4rMQBUW1v8exqdWmBSWAj026dzMqiA69xOE12yTYaBrlnKiBQ31nGh3ChYfNfZz9P2cFa6uxptrE7XdrTcBxr+SbZ+TEGL8eMDfL8FzDi1x9ePqZrCsQzmYFJ3Xadls19EFSF1QprFEkndSUbQS1J1/C4n1oL6JU3rQJpcoHwwjwHycVqN15ivYcHUGkfS6guIvctzUiGwA+EG0bITjLVMiHlIAdT/h5FRq+VoKnVVLeADe+Lt+omiSRQ3ImKL7VP7/By3iTQ+oD/YoYDDCJyPcAiuaMIIeB14xIrSKtH3pIrEhWtEf9AASZWttYG44ZrD+dmg9qSPqSv5ABd2zpr8yiYur7L074uqgs1FrmqVJzehOpw4CFz808LJtT/7kAG9XJIa8uSRlXnTkZMhHQ0rhondJEPTa0X9NjzQuYykhnvMKKbO61NFKxWYvjVPW2ggm7Qld6V91SINJCH82h2suWB14o4WWZVtKnjfbzahkppimasPaY1jDspfsq71rozuaXAcHjgWJG9LPtnAN3+3YKpZm65pumtnmHAa49qME7qXL2ZZNATe/DVyBZENULabZUKXBGzD/x088ATjDXzfSoDcPOvQRRRwsI/kYL5YHrEix3EIEOmqgtDZd6vs+HkeQ2CEk92WpPRMeOhoGoz72ZDRF5yejQsP9WOSptfp8J61B/ChLYVlZyigpcP7CoebOJsKb2aGwJzzNyT4TPAYZO/PtpONAgwnaXVJgIhPzW3wRk6CUPQZoKoPbmXmflzXL5Kw5XdKXvcFEXmT2Uc32hxEsB8aQSTgXtPH6QNRBR2b7RO+qnGSe/St1YL8bY6AH5x6SY7HaB++zwR4l3119pjfGB6SF5ZQKWolMY1sA3mEgdMtGhdqj2QbqZydFA6NsH+MleKt5zaw8y4al8lZ/4131YdeFiJQev9gOSw0MovYo53GxNNmXAAqfsz3egEg376e6dFE7z3ZSXk5jd3rbpO+wKeb4cBWiutu+rhMaFyRYknT+D6WsH/bUpsYOVEH4nD5RVVrz9OFArklDuvtIhph8lMs6rdyEsucjMacXl7GTkdXUfktkMU4vQjoMe1vEiJrWPGoh+bFLiVn6rUS49NdFQK/cwy8z+ifWt340Okl7ZuYKkMAAq7ROUU8gSZVDXUvz0BavwFAqwcX2G61ZLqgLsiy/in2i2XVd1iW8PDykgLU1yYR3SA== X-OriginatorOrg: corigine.com X-MS-Exchange-CrossTenant-Network-Message-Id: 415d87fa-5800-49ff-c666-08dc15755a8d X-MS-Exchange-CrossTenant-AuthSource: SJ0PR13MB5545.namprd13.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 15 Jan 2024 02:54:56.1280 (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: pMoDZ6nhmoJOgfihfUeH7vVHs4dsIT0/zIVhIrZxGRTFWxH6yB1xfvXuyWY1mr/nz0VI3cFZBafwZGkeXAV6G9RgZLuQ6O2tIQCbOmIN5lE= X-MS-Exchange-Transport-CrossTenantHeadersStamped: BLAPR13MB4578 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 elf module, which can get mip 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 | 1078 +++++++++++++++++++++++++++++ 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, 1167 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 46be6f60cd..a4060b4cd5 100644 --- a/drivers/net/nfp/meson.build +++ b/drivers/net/nfp/meson.build @@ -17,6 +17,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..2826fcd410 --- /dev/null +++ b/drivers/net/nfp/nfpcore/nfp_elf.c @@ -0,0 +1,1078 @@ +/* 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_flie_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_flie_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_buildtime(uint32_t *fw_buildtime, + 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_buildtime = elf->fw_mip.fw_buildtime; + + 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..5b1099fb70 --- /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_buildtime(uint32_t *fw_buildtime, 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..c6a25b6387 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_buildtime(const struct nfp_mip *mip) +{ + return mip->buildtime; +} + /** * 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..a67afdc1ea 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_le_to_cpu_32(0x0050494d) +#define NFP_MIP_VERSION rte_le_to_cpu_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_buildtime(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);