From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <dev-bounces@dpdk.org>
Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124])
	by inbox.dpdk.org (Postfix) with ESMTP id A017145AFE;
	Thu, 10 Oct 2024 08:42:39 +0200 (CEST)
Received: from mails.dpdk.org (localhost [127.0.0.1])
	by mails.dpdk.org (Postfix) with ESMTP id 56DC4402ED;
	Thu, 10 Oct 2024 08:42:35 +0200 (CEST)
Received: from NAM11-CO1-obe.outbound.protection.outlook.com
 (mail-co1nam11on2108.outbound.protection.outlook.com [40.107.220.108])
 by mails.dpdk.org (Postfix) with ESMTP id DF864400EF
 for <dev@dpdk.org>; Thu, 10 Oct 2024 08:42:32 +0200 (CEST)
ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;
 b=HzqYSxCmO7R9P7QQsA1W4gYP1+jgW+GZPPQOnYBJQOICjyUCEbq8dugxVzIjuIMnVssAQO29cuEJjpQa0Ozdr2wjyF9pOj7Tve8nPupCZeWwNdllHGBf+7MheNk2AP9eDDVRVCCMm9bQ/T2FKvFJBv/lKiIhnjgd6zqiEmcGRVyBuwqHXYP1kyK78VRJ8kRdQjzX48nvGBYl2h4wW5y6vNDstpxr3dKEEm7D+TiK8Y9WXpA24Th0JFE8Rb7aIF4Q0LKXwUq4XlnSczzPfadX7cJtOF+v1phNGqRNN/x2U/6YH1b4heK5JyhGZ7p8osWMRxpwH9/5dWu2y/Qcbua7Rw==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; 
 s=arcselector10001;
 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=kJfmg2GaB2/2d3Wf8/dP8e/4XHj8e/90N/n9q02+dTI=;
 b=GtOhjPNQVt3AdpGt+0imCnEnADP/so8YhA/QyL8rH6gaQVd2YFKed0uyTqdoVuYLHTPDZU2sRiH02gJC7lBgx4tqkmgsVVwvNqDPQBQyKN7QeR7n367SuKRzn74KcuavQoqaeiefJc51fMy8nfEvnrL95YKqkAy9dkS9IQd2p3l0AY663+7NKhDA8ZJ90Z/rZkBtuGLHolYnDq2a8I4XmNorSn8R0sFBoc3lkoubCxJrqGMraIKH2pcWftLFntLB6ge5fpeIRQglYC/toRZqWZQh0ewbpyhj+JAsHpYAHM9gLB897SfvTYd4+1+xoYfJn4ZtaZ+kOzirC7DsyhUugw==
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=kJfmg2GaB2/2d3Wf8/dP8e/4XHj8e/90N/n9q02+dTI=;
 b=ZPRdKd1DQWQ1UgNKeHW+AXNv8TdTyiknp81G1+05KcElhJrHBUOkzwT3uf8J38kyrezwcJ0xdonqeW9mXm1f5d2U5AXgvh4mkqkmHsD3z0/W0RFa+QUEGIjiFXN9JvClNvp9Zi6znoPN1JqiN0zLtb7epPldLhKKlY3+x9Dt2oY=
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 PH7PR13MB6092.namprd13.prod.outlook.com (2603:10b6:510:2a5::7)
 with Microsoft SMTP Server (version=TLS1_2,
 cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8026.22; Thu, 10 Oct
 2024 06:42:29 +0000
Received: from SJ0PR13MB5545.namprd13.prod.outlook.com
 ([fe80::b900:5f05:766f:833]) by SJ0PR13MB5545.namprd13.prod.outlook.com
 ([fe80::b900:5f05:766f:833%5]) with mapi id 15.20.8026.020; Thu, 10 Oct 2024
 06:42:29 +0000
From: Chaoyong He <chaoyong.he@corigine.com>
To: dev@dpdk.org
Cc: oss-drivers@corigine.com, James Hershaw <james.hershaw@corigine.com>,
 Chaoyong He <chaoyong.he@corigine.com>
Subject: [PATCH v5 1/2] app/testpmd: add support for setting device EEPROM
Date: Thu, 10 Oct 2024 14:42:05 +0800
Message-Id: <20241010064206.3527959-2-chaoyong.he@corigine.com>
X-Mailer: git-send-email 2.39.1
In-Reply-To: <20241010064206.3527959-1-chaoyong.he@corigine.com>
References: <20240918023847.2964100-1-chaoyong.he@corigine.com>
 <20241010064206.3527959-1-chaoyong.he@corigine.com>
Content-Transfer-Encoding: 8bit
Content-Type: text/plain
X-ClientProxiedBy: SJ0PR03CA0122.namprd03.prod.outlook.com
 (2603:10b6:a03:33c::7) To SJ0PR13MB5545.namprd13.prod.outlook.com
 (2603:10b6:a03:424::5)
MIME-Version: 1.0
X-MS-PublicTrafficType: Email
X-MS-TrafficTypeDiagnostic: SJ0PR13MB5545:EE_|PH7PR13MB6092:EE_
X-MS-Office365-Filtering-Correlation-Id: fb826ee8-9a7e-4d99-de07-08dce8f6b621
X-MS-Exchange-SenderADCheck: 1
X-MS-Exchange-AntiSpam-Relay: 0
X-Microsoft-Antispam: BCL:0;
 ARA:13230040|1800799024|366016|376014|52116014|38350700014; 
X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?n+GjhS12WBYDOEjvR915JhhaY4f+In3ud8ZEiJdME9bRtSZgmrKhkZleMRpM?=
 =?us-ascii?Q?96VKCndBGQ87p1vuqbMtftQJO0NGGpCzIJ+ZNMf+F0ziLCV0GE+n/WfsQ37D?=
 =?us-ascii?Q?bXVDx/AnhJ/XcyuWm6dJsV1wuMlCj929ribG5DGMW09m++qMi/D4CsJ+5Vnj?=
 =?us-ascii?Q?G5giynu45shIddvF0rzTnqYkJi7DW8n32QMHdzydY32L6+pt9lwqY5XQKCRs?=
 =?us-ascii?Q?4dZyKBLhGcztnJTldZKl1fJzu79ElfHXcaE2g59T4wkKkS6P+Qmnfuk/3bdT?=
 =?us-ascii?Q?kzW7u46MQB7G2uz4JDHS4KFr6PYggfmCHOBSB601wvwZ+N+8hFm6dy7hEbkn?=
 =?us-ascii?Q?ccsbhi7+Q3fPhc/iwKP1TyR/v+WRQS1fZHmJAWmTnQfSyKa0g8/CgX9c+WPJ?=
 =?us-ascii?Q?z1XnKOxioTfd9vCQORxuMwYZ2Sjt+qYKKt+IXhJBebOrgHZM4BG2CBhEMB/C?=
 =?us-ascii?Q?1UNvkSbuGlEqF0KJh2HnBJ4QZ0OaV58tsnmDfBdsPX8fTq8ZXWpKnfjvtluM?=
 =?us-ascii?Q?JO8y3Wb9yoBwHM+wd1wKbNkb7+xqJLjMI6lvnFQSUCvYtaBavZGk6AEXZ2LQ?=
 =?us-ascii?Q?l9HydlEof/Ga62Wdm1Yuq88JH0zJu+qEo3nKk/6u788VeZRQjTcGtRAIG2w1?=
 =?us-ascii?Q?PGdRSOjtwPSdclcQBKTIFPK+2fExmRC/gesxFODKgO1MfrdMavS7PdT0j4qh?=
 =?us-ascii?Q?n6pmwEx23d0HrkKSxneB1YKKyqgWRQZppWkVW/focxo4Y4wpgwqQS9cYjs6c?=
 =?us-ascii?Q?pQEbNZ0fgLMAximJMflwvgsmpGXSQ7miT8C4bvrjKZ7HapzYuA50/vGxKuCF?=
 =?us-ascii?Q?nqpta8p1/4qV6jSSqxaX8CaZ8zwyMo+4m9ymGV+KTylyAGZZaEfCk2RrCZ7l?=
 =?us-ascii?Q?H14RI+Uu1X/a6ay9wkxNTr1/qj6jZUkIM6wA2rvv6dX/w2rLD0pH/qRR6Zk6?=
 =?us-ascii?Q?uzWZLNd158M8RqoXpvuI6yMf8p1kTIG8F5UNfutmeUiDAiJoMZSrytH15X53?=
 =?us-ascii?Q?x8/e24/6873tIhD5UxJOcETxM9LpgdL1AaH21GrhhGsVRO1tQyNkdFbgtrDo?=
 =?us-ascii?Q?SCvLsRFFYf2M/73V2okCCO297bV4WFoIKDSlVZ7XFHHCtdYq2OYspE8lqCLT?=
 =?us-ascii?Q?NzvsC9vGpaQJ1z7AwIuHxHwpq0CI61v/RnE/ic5hqJI/zAdRAwyQjKRKPWwG?=
 =?us-ascii?Q?Xa/ZT0ILoU+rKylZ3SG3fMWfMt4DZGZqwDeZWVZFAf1D3XyVyfrs1Fv8JuK/?=
 =?us-ascii?Q?8UqTri5Ry61FTvMmQVB4/zxcW4ednB3yJGm44lag0VkRC4Ym93DkV6maNaK8?=
 =?us-ascii?Q?KtHcTh7s912MrzwyuaUeBE/ArD7DpNQF+VG+7bo3o/v+6g=3D=3D?=
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:(13230040)(1800799024)(366016)(376014)(52116014)(38350700014); DIR:OUT;
 SFP:1102; 
X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1
X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?Ex+0FDeMXizFq206fJRVf6RgPWt6evbMRNBqmxmfa5ccyKoZajHKxrfb+nrg?=
 =?us-ascii?Q?yCwx7yX7/1ZfYuNcaXozeYv+vDRlsBuT3iYFJ07b76RS1mrRO6icTTHkm1Lf?=
 =?us-ascii?Q?F4JXnmZYH/v6BW0ABkQ8HVwhHmNrYOapkx1He8Iv5fJmBnriQ+HCdeCoY/Oi?=
 =?us-ascii?Q?HKcQQww3BjyQCcuG9rWsmnI+WUZt9dqMMHxV3XoXnr/3DcMpZIISVWk07jsd?=
 =?us-ascii?Q?f53m6K7g5VSfxrzNcRk7y8ujFL8XCQcoHl7rZWtDxSB7pM7jN0GO4EoDVKiN?=
 =?us-ascii?Q?dF/fakfP5uZREQjBVM/IRGaUcVlPSUi0TAmOOc1+AWKqDJbGs61cQecFnJ9P?=
 =?us-ascii?Q?OSfT/2NBQ8Oevmr83m4BAKtW7XJ1gLy0iCUy3QQZAWbcYy1Gpv7/lAlhfBta?=
 =?us-ascii?Q?DQtOPtgXGM34x1Kh5Ad6VG7XkiccEDuqc6k7t1hCYnB89Au28s23sBs6+Sj2?=
 =?us-ascii?Q?vcs1Gtu9irXRlFf6VBe/BKQIRcPuGLnce2wizQEE6Vj0oVv+DygmrWxMTglh?=
 =?us-ascii?Q?7SCBKOqocHFM3nIIBgc7dJv+ctn39Y5Rr5Pa/MeG5AKNzaW5uDhHZ2wuL86g?=
 =?us-ascii?Q?tgpuqQEdNaYU8mrq6ouJMUSzr7CvbHybT6oHUtlzpnqBhZ13h41MmGPQcyDA?=
 =?us-ascii?Q?54eJKS29c8snhmPNdooXwXv7q36HoDVu4Q3Ruy8bMWBFIvriFzbnd2lZuLFz?=
 =?us-ascii?Q?xvGi+oXpVqlLOhRzUb6wQVvNhJjusV9lhc9UN0J2CjentC/yScOeTRe6QOlR?=
 =?us-ascii?Q?hM9xVrf3xQ+iG3/IpsBbCAkglXHhia3TshUK4wZKMRv9NCH106PAq5nwsJts?=
 =?us-ascii?Q?+dYyKu8OIl2lAkeWOi3AYndY/fvhb6SLHIBev89EGcZdUUOvocCcH9OEu+mW?=
 =?us-ascii?Q?2EUqtsz16z+0N/VETpmzfkERf1j18BX+FLmfliLreNQztiJ1w+4Ks94rl6o+?=
 =?us-ascii?Q?SZ98mRsDIbpUnWC86B5UNCi3bpmJtCqNsd9X8hcHq3V6JEELYAXbw4AgU5bM?=
 =?us-ascii?Q?xil4AIAwQYABRZCM1QEuV3LX5bqE8ioIMEB2HjTpws03pQkTxrcvNFACEx5l?=
 =?us-ascii?Q?GsCgEKskMJvViZpIO73ElA254EdYhWBscCdSwYW4B2s6UNv2bOrw//4uzU/4?=
 =?us-ascii?Q?CDTyKVH53jOzcINBjFHOvhNSA36daWSm6p5h9dOGFUIP/yQrWwra0TFDz95f?=
 =?us-ascii?Q?IjgrCQibYaICFE+wOOGEeUst+tp/Ycd/WeRLVG0qQ4FqQExBU11i9ZSelYQP?=
 =?us-ascii?Q?0d0dVCKiozs5PMDqQeI593t6f7iam7WMowMPx3NP2VnNepe8BdNeXfOvx9nH?=
 =?us-ascii?Q?eN0SqayVhjcoNqUOjwE5KBReCd6SgtOrvT87il7ZrJfa8UkEHcgRw7wdzmM3?=
 =?us-ascii?Q?Vu0z3japQ/LeGxuYxpMeRarW6TVfupoG3SDat/vaDCse1JDMsNn+gO29q0eL?=
 =?us-ascii?Q?oxGQ1JJS7sSfny52q70f5sfdgaFZ/r+ozuBsI0lhd7MKuGcJAZ7MxMRoxbxs?=
 =?us-ascii?Q?s7iKuCG3hLmFP5iwJEKo3Hi6kKKAkitEkVizf8VOhK70otTQz+H4EM7+kD+j?=
 =?us-ascii?Q?eh/pqht9OwBSmvUyo3Di/WU5KgNX0MNUwZUjWvBX7AGaLFgtCOx8i8plSaKk?=
 =?us-ascii?Q?wA=3D=3D?=
X-OriginatorOrg: corigine.com
X-MS-Exchange-CrossTenant-Network-Message-Id: fb826ee8-9a7e-4d99-de07-08dce8f6b621
X-MS-Exchange-CrossTenant-AuthSource: SJ0PR13MB5545.namprd13.prod.outlook.com
X-MS-Exchange-CrossTenant-AuthAs: Internal
X-MS-Exchange-CrossTenant-OriginalArrivalTime: 10 Oct 2024 06:42:29.7913 (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: cvyJzd9OdVt5ld5dmBfEruNQiODY438BroMM+fMciHyHVfOV2nCuy/t6QGXPRK6tGnqnJjfYkum+cffdZwhYYbGA0cr+RbYfGScDwoTQdwU=
X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH7PR13MB6092
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <https://mails.dpdk.org/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://mails.dpdk.org/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <https://mails.dpdk.org/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
Errors-To: dev-bounces@dpdk.org

From: James Hershaw <james.hershaw@corigine.com>

There is currently no means to test the .set_eeprom function callback of
a given PMD in drivers/net/. This patch adds functionality to allow a
user to set device eeprom from the testpmd cmdline.

Usage:
  testpmd> set port <port-id> eeprom <accept_risk> magic <magic> \
  		value <value> offset <offset>

- <accept_risk> is a fixed string that is required from the user to
  acknowledge the risk involved in using this command and accepting the
  responsibility for that.
- <magic> is a decimal.
- <value> is a hex-as-string with no leading "0x".
- <offset> is a decimal (this field is optional and defaults to 0 if
  not specified.)

Signed-off-by: James Hershaw <james.hershaw@corigine.com>
Reviewed-by: Chaoyong He <chaoyong.he@corigine.com>
---
 app/test-pmd/cmdline.c                      | 127 ++++++++++++++++++++
 app/test-pmd/config.c                       |  39 ++++++
 app/test-pmd/testpmd.h                      |   2 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  18 +++
 4 files changed, 186 insertions(+)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 1fc1cce5fe..c917c4e83b 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -170,6 +170,15 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"show port port_id (module_eeprom|eeprom)\n"
 			"    Display the module EEPROM or EEPROM information for port_id.\n\n"
 
+			"set port (port_id) eeprom (accept_risk) magic (magic_num)"
+			" value (value) offset (offset)\n"
+			"    Set the device eeprom for certain port.\nNote:\n"
+			"    This is a high-risk command and its misuse may"
+			" result in unexpected behaviour from the NIC.\n"
+			"    By inserting \"accept_risk\" into the command, the"
+			" user is acknowledging and taking responsibility for"
+			" this risk.\n\n"
+
 			"show port X rss reta (size) (mask0,mask1,...)\n"
 			"    Display the rss redirection table entry indicated"
 			" by masks on port X. size is used to indicate the"
@@ -7462,6 +7471,123 @@ static cmdline_parse_inst_t cmd_showeeprom = {
 	},
 };
 
+/* *** SET PORT EEPROM *** */
+struct cmd_seteeprom_result {
+	cmdline_fixed_string_t set;
+	cmdline_fixed_string_t port;
+	uint16_t portnum;
+	cmdline_fixed_string_t eeprom;
+	cmdline_fixed_string_t confirm_str;
+	cmdline_fixed_string_t magic_str;
+	uint32_t magic;
+	cmdline_fixed_string_t value;
+	cmdline_multi_string_t token_str;
+};
+
+static void cmd_seteeprom_parsed(void *parsed_result,
+		__rte_unused struct cmdline *cl,
+		__rte_unused void *data)
+{
+	struct cmd_seteeprom_result *res = parsed_result;
+	uint32_t offset = 0;
+	uint32_t length;
+	uint8_t *value;
+	char *token_str;
+	char *token;
+
+	token_str = res->token_str;
+	token = strtok_r(token_str, " \f\n\r\t\v", &token_str);
+
+	/* Parse Hex string to Byte data */
+	if (strlen(token) % 2 != 0) {
+		fprintf(stderr, "Bad Argument: %s\nHex string must be in multiples of 2 Bytes\n",
+			token);
+		return;
+	}
+
+	length = strlen(token) / 2;
+	value = calloc(length, sizeof(uint8_t));
+	for (int count = 0; count < (int)(length); count++) {
+		if (sscanf(token, "%2hhx", &value[count]) != 1) {
+			fprintf(stderr, "Bad Argument: %s\nValue must be a hex string\n",
+				token - (count + 1));
+			goto out;
+		}
+		token += 2;
+	}
+
+	/* Second token: offset string */
+	token = strtok_r(token_str, " \f\n\r\t\v", &token_str);
+	if (token != NULL) {
+		if (strcmp(token, "offset") == 0) {
+			/* Third token: offset */
+			token = strtok_r(token_str, " \f\n\r\t\v", &token_str);
+			if (token == NULL) {
+				fprintf(stderr, "No offset specified\n");
+				goto out;
+			}
+
+			char *end;
+			offset = strtoul(token, &end, 10);
+			if (offset == 0 && strcmp(end, "") != 0) {
+				fprintf(stderr, "Bad Argument: %s\n", token);
+				goto out;
+			}
+		} else {
+			fprintf(stderr, "Bad Argument: %s\n", token);
+			goto out;
+		}
+	}
+
+	port_eeprom_set(res->portnum, res->magic, offset, length, value);
+
+out:
+	free(value);
+}
+
+static cmdline_parse_token_string_t cmd_seteeprom_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_seteeprom_result, set, "set");
+static cmdline_parse_token_string_t cmd_seteeprom_port =
+	TOKEN_STRING_INITIALIZER(struct cmd_seteeprom_result, port, "port");
+static cmdline_parse_token_num_t cmd_seteeprom_portnum =
+	TOKEN_NUM_INITIALIZER(struct cmd_seteeprom_result, portnum, RTE_UINT16);
+static cmdline_parse_token_string_t cmd_seteeprom_eeprom =
+	TOKEN_STRING_INITIALIZER(struct cmd_seteeprom_result, eeprom, "eeprom");
+static cmdline_parse_token_string_t cmd_seteeprom_confirm_str =
+	TOKEN_STRING_INITIALIZER(struct cmd_seteeprom_result, confirm_str, "accept_risk");
+static cmdline_parse_token_string_t cmd_seteeprom_magic_str =
+	TOKEN_STRING_INITIALIZER(struct cmd_seteeprom_result, magic_str, "magic");
+static cmdline_parse_token_num_t cmd_seteeprom_magic =
+	TOKEN_NUM_INITIALIZER(struct cmd_seteeprom_result, magic, RTE_UINT32);
+static cmdline_parse_token_string_t cmd_seteeprom_value =
+	TOKEN_STRING_INITIALIZER(struct cmd_seteeprom_result, value, "value");
+static cmdline_parse_token_string_t cmd_seteeprom_token_str =
+	TOKEN_STRING_INITIALIZER(struct cmd_seteeprom_result, token_str, TOKEN_STRING_MULTI);
+
+static cmdline_parse_inst_t cmd_seteeprom = {
+	.f = cmd_seteeprom_parsed,
+	.data = NULL,
+	.help_str = "set port <port_id> eeprom <accept_risk> magic <magic_num> "
+		"value <value> offset <offset>: Set eeprom value for port_id.\n"
+		"Note:\n"
+		"This is a high-risk command and its misuse may result in "
+		"unexpected behaviour from the NIC.\n"
+		"By inserting \"accept_risk\" into the command, the user is "
+		"acknowledging and taking responsibility for this risk.",
+	.tokens = {
+		(void *)&cmd_seteeprom_set,
+		(void *)&cmd_seteeprom_port,
+		(void *)&cmd_seteeprom_portnum,
+		(void *)&cmd_seteeprom_eeprom,
+		(void *)&cmd_seteeprom_confirm_str,
+		(void *)&cmd_seteeprom_magic_str,
+		(void *)&cmd_seteeprom_magic,
+		(void *)&cmd_seteeprom_value,
+		(void *)&cmd_seteeprom_token_str,
+		NULL,
+	},
+};
+
 /* *** SHOW QUEUE INFO *** */
 struct cmd_showqueue_result {
 	cmdline_fixed_string_t show;
@@ -13397,6 +13523,7 @@ static cmdline_parse_ctx_t builtin_ctx[] = {
 	&cmd_showport,
 	&cmd_showqueue,
 	&cmd_showeeprom,
+	&cmd_seteeprom,
 	&cmd_showportall,
 	&cmd_representor_info,
 	&cmd_showdevice,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 1d23feec2a..37f074a69b 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1066,6 +1066,45 @@ port_eeprom_display(portid_t port_id)
 	free(einfo.data);
 }
 
+void
+port_eeprom_set(portid_t port_id,
+		uint32_t magic,
+		uint32_t offset,
+		uint32_t length,
+		uint8_t *value)
+{
+	struct rte_dev_eeprom_info einfo;
+	int len_eeprom;
+	int ret;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN)) {
+		print_valid_ports();
+		return;
+	}
+
+	len_eeprom = rte_eth_dev_get_eeprom_length(port_id);
+	if (len_eeprom < 0) {
+		fprintf(stderr, "Unable to get EEPROM length: %s\n",
+			rte_strerror(-len_eeprom));
+		return;
+	}
+
+	einfo.data = value;
+	einfo.magic = magic;
+	einfo.length = length;
+	einfo.offset = offset;
+
+	if (einfo.offset + einfo.length > (uint32_t)(len_eeprom)) {
+		fprintf(stderr, "offset and length exceed capabilities of EEPROM length: %d\n",
+			len_eeprom);
+		return;
+	}
+
+	ret = rte_eth_dev_set_eeprom(port_id, &einfo);
+	if (ret != 0)
+		fprintf(stderr, "Unable to set EEPROM: %s\n", rte_strerror(-ret));
+}
+
 void
 port_module_eeprom_display(portid_t port_id)
 {
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index f9ab88d667..56ab7ef1db 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -927,6 +927,8 @@ void device_infos_display(const char *identifier);
 void port_infos_display(portid_t port_id);
 void port_summary_display(portid_t port_id);
 void port_eeprom_display(portid_t port_id);
+void port_eeprom_set(portid_t port_id, uint32_t magic, uint32_t offset,
+		     uint32_t length, uint8_t *value);
 void port_module_eeprom_display(portid_t port_id);
 void port_summary_header_display(void);
 void rx_queue_infos_display(portid_t port_idi, uint16_t queue_id);
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index f00ab07605..eb19e747b5 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -223,6 +223,24 @@ Display the EEPROM information of a port::
 
    testpmd> show port (port_id) (module_eeprom|eeprom)
 
+set eeprom
+~~~~~~~~~~
+
+Write a value to the device eeprom of a port at a specific offset::
+
+   testpmd> set port (port_id) eeprom (accept_risk) magic (magic_num) value (value) \
+            offset (offset)
+
+Value should be given in the form of a hex-as-string, with no leading ``0x``.
+The offset field here is optional, if not specified then the offset will default
+to 0.
+
+.. note::
+
+   This is a high-risk command and its misuse may result in unexpected
+   behaviour from the NIC. By inserting "accept_risk" into the command, the user
+   is acknowledging and taking responsibility for this risk.
+
 show port rss reta
 ~~~~~~~~~~~~~~~~~~
 
-- 
2.39.1