[Skiboot] [PATCH 8/8] core/pldm/test : Add PLDM FRU table self test

ABHISHEK SINGH TOMAR abhishek.singh.tomar1 at ibm.com
Thu Feb 20 20:34:55 AEDT 2025


From: Abhishek Singh Tomar <abhishek at linux.ibm.com>

Add self-test functionality for PLDM FRU commands
1. pldm_get_fru_record_table
	— Validates both success and failure cases.
2. pldm_fru_set_local_table and pldm_fru_get_local_table
	— Ensures correct functionality of these table operations.

Signed-off-by: Abhishek Singh Tomar <abhishek at linux.ibm.com>
---
 core/pldm/test/Makefile.check  |   3 +-
 core/pldm/test/test-pldm-fru.c | 415 +++++++++++++++++++++++++++++++++
 2 files changed, 417 insertions(+), 1 deletion(-)
 create mode 100644 core/pldm/test/test-pldm-fru.c

diff --git a/core/pldm/test/Makefile.check b/core/pldm/test/Makefile.check
index 10a1752c0..1135b8e5d 100644
--- a/core/pldm/test/Makefile.check
+++ b/core/pldm/test/Makefile.check
@@ -1,7 +1,8 @@
 # -*-Makefile-*-
 CUR_DIR := core/pldm/test/
 PLDM_TEST := core/pldm/test/test-pldm-fileio-bios \
-	     core/pldm/test/test-pldm-platform
+	     core/pldm/test/test-pldm-platform \
+	     core/pldm/test/test-pldm-fru
 LCOV_EXCLUDE += $(PLDM_TEST:%=%.c)
 
 .PHONY : core-pldm-check core-pldm-coverage
diff --git a/core/pldm/test/test-pldm-fru.c b/core/pldm/test/test-pldm-fru.c
new file mode 100644
index 000000000..9116dc4b7
--- /dev/null
+++ b/core/pldm/test/test-pldm-fru.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+// Copyright 2024 IBM Corp.
+
+#include "test-pldm-common.c"
+
+#define FRU1_MODEL "9309-24B"
+#define FRU1_SERIAL "1234567"
+#define FRU1_NAME "System"
+#define FRU1_VERSION "fw10xx-test"
+
+enum fru_special_case_code  {
+	NORMAL_CASE = 0x00,
+	FRU_TABLE_TRANSFER_FLAG_ERROR = 0x01,
+	FRU_TABLE_RESPONSE_ERROR = 0x02,
+};
+enum fru_special_case_code  fru_special_case = NORMAL_CASE;
+
+
+/*
+ * Test Fru Table data
+ */
+struct pldm_fru_record_data_format fru1_test = {
+	.record_set_id = 1,
+	.record_type = PLDM_FRU_RECORD_TYPE_GENERAL,
+	.num_fru_fields = 4,
+	.encoding_type = PLDM_FRU_ENCODING_ASCII
+};
+
+#define FRU2_NAME "FRU_test"
+#define FRU2_LOCATION_CODE "U78AA.mmm"
+struct pldm_fru_record_data_format fru2_test = {
+	.record_set_id = 2,
+	.record_type = PLDM_FRU_RECORD_TYPE_OEM,
+	.num_fru_fields = 2,
+	.encoding_type = PLDM_FRU_ENCODING_ASCII
+};
+
+/*
+ * This function will encode given tlv data
+ * to tlvs buffer at offset
+ * and update offset to end of tlvs
+ */
+static int encode_tlv_buff(uint8_t *tlvs, size_t *offset,
+		uint8_t type, uint8_t length, const uint8_t *value)
+{
+	struct pldm_fru_record_tlv *tlv;
+
+	tlv = (struct pldm_fru_record_tlv *)(tlvs + *offset);
+	tlv->type = type;
+	tlv->length = length;
+	memcpy(tlv->value, value, tlv->length);
+	*offset += sizeof(struct pldm_fru_record_tlv) + tlv->length - 1;
+
+	return OPAL_SUCCESS;
+}
+
+/*
+ * This function duplicates BMC functionality for Pldm self test
+ * This Genrate FRU Table entry for self test
+ * The fru table contains the list of fru records available.
+ */
+static int get_test_fru_table(uint8_t **fru_table, size_t *fru_table_len)
+{
+	int rc;
+	size_t record_len;
+	uint8_t *tlvs_record1;
+	uint8_t *tlvs_record2;
+	size_t tlvs_record1_len = 0;
+	size_t tlvs_record2_len = 0;
+	size_t offset;
+
+	/*
+	 * Generate FRU 1 record
+	 * FRU 1 record have 4 tlv
+	 * in our test scanerio
+	 */
+	tlvs_record1_len += 4*(sizeof(struct
+				pldm_fru_record_tlv) - 1);
+	tlvs_record1_len += strlen(FRU1_MODEL) + strlen(FRU1_SERIAL)
+		+ strlen(FRU1_NAME) + strlen(FRU1_VERSION);
+
+	tlvs_record1 = malloc(tlvs_record1_len);
+	if (tlvs_record1 == NULL) {
+		perror("PLDM_TEST malloc");
+		return OPAL_RESOURCE;
+	}
+	offset = 0;
+	encode_tlv_buff(tlvs_record1, &offset, PLDM_FRU_FIELD_TYPE_MODEL,
+			strlen(FRU1_MODEL), FRU1_MODEL);
+	encode_tlv_buff(tlvs_record1, &offset, PLDM_FRU_FIELD_TYPE_SN,
+			strlen(FRU1_SERIAL), FRU1_SERIAL);
+	encode_tlv_buff(tlvs_record1, &offset, PLDM_FRU_FIELD_TYPE_NAME,
+			strlen(FRU1_NAME), FRU1_NAME);
+	encode_tlv_buff(tlvs_record1, &offset, PLDM_FRU_FIELD_TYPE_VERSION,
+			strlen(FRU1_VERSION), FRU1_VERSION);
+	/*
+	 * Check if offset equal to size calculated
+	 */
+	if (offset != tlvs_record1_len) {
+		free(tlvs_record1);
+		return OPAL_PARAMETER;
+	}
+
+	/*
+	 * Generate FRU 2 record
+	 * FRU 2 record have 2 tlv
+	 * in our test scanerio
+	 */
+	tlvs_record2_len += 2*(sizeof(struct
+				pldm_fru_record_tlv) - 1);
+	tlvs_record2_len += strlen(FRU2_NAME) + strlen(FRU2_LOCATION_CODE);
+
+	tlvs_record2 = malloc(tlvs_record2_len);
+	if (tlvs_record2 == NULL) {
+		perror("PLDM_TEST malloc");
+		free(tlvs_record1);
+		return OPAL_RESOURCE;
+	}
+	offset = 0;
+	encode_tlv_buff(tlvs_record2, &offset, PLDM_FRU_FIELD_TYPE_NAME,
+			strlen(FRU2_NAME), FRU2_NAME);
+	encode_tlv_buff(tlvs_record2, &offset, 0XFE, strlen(FRU2_LOCATION_CODE),
+			FRU2_LOCATION_CODE);
+	/*
+	 * Check if offset equal to size calculated
+	 */
+	if (offset != tlvs_record2_len) {
+		free(tlvs_record1);
+		free(tlvs_record2);
+		return OPAL_PARAMETER;
+	}
+
+	/*
+	 * Allocate memory for fru table
+	 * which include space for both
+	 * FRU records
+	 */
+	*fru_table_len = 2 * (sizeof(struct pldm_fru_record_data_format) - 1)
+		+ tlvs_record1_len + tlvs_record2_len;
+
+	*fru_table = malloc(*fru_table_len);
+	if (*fru_table == NULL) {
+		perror("PLDM_TEST malloc");
+		free(tlvs_record1);
+		free(tlvs_record2);
+		return OPAL_RESOURCE;
+	}
+
+	offset = 0;
+
+	/*
+	 * Encode FRU 1 record to
+	 * fru table
+	 */
+	record_len = sizeof(struct pldm_fru_record_data_format) +
+		tlvs_record1_len - sizeof(struct pldm_fru_record_tlv);
+
+	rc = encode_fru_record(*fru_table,
+			offset + record_len, &offset, fru1_test.record_set_id,
+			fru1_test.record_type, fru1_test.num_fru_fields,
+			fru1_test.encoding_type, tlvs_record1, tlvs_record1_len);
+	if (rc != OPAL_SUCCESS) {
+		perror("PLDM_TEST encode_fru_record");
+		free(tlvs_record1);
+		free(tlvs_record2);
+		return OPAL_PARAMETER;
+	}
+
+	/*
+	 * Encode FRU 2 record to
+	 * fru table
+	 */
+	record_len = sizeof(struct pldm_fru_record_data_format) +
+		tlvs_record2_len - sizeof(struct pldm_fru_record_tlv);
+	rc = encode_fru_record(*fru_table,
+			offset + record_len, &offset, fru2_test.record_set_id,
+			fru2_test.record_type, fru2_test.num_fru_fields,
+			fru2_test.encoding_type, tlvs_record2, tlvs_record2_len);
+	if (rc != OPAL_SUCCESS) {
+		perror("PLDM_TEST encode_fru_record");
+		free(tlvs_record1);
+		free(tlvs_record2);
+		return OPAL_PARAMETER;
+	}
+
+	free(tlvs_record1);
+	free(tlvs_record2);
+	return OPAL_SUCCESS;
+}
+
+
+/*
+ * This function duplicates BMC functionality for Pldm self test
+ * it handle PLDM_REQUEST for PLDM_PLATFORM and reply with appropriate
+ * PLDM_RESPONSE message
+ */
+int pldm_test_reply_request_fru(void *request_msg, size_t request_len,
+		void **response_msg, size_t *response_len)
+{
+	int rc = 0;
+	int  payload_len = 0, completion_code = PLDM_SUCCESS;
+	uint32_t data_transfer_handle, next_data_transfer_handle;
+	uint8_t transfer_operation_flag;
+	uint8_t *fru_table;
+	size_t fru_table_len;
+	struct pldm_get_fru_record_table_resp *resp_payload;
+
+	/* check pldm command received and reply with appropriate pldm response message */
+	switch (((struct pldm_msg *)request_msg)->hdr.command) {
+	case PLDM_GET_FRU_RECORD_TABLE:
+		payload_len = request_len - sizeof(struct pldm_msg_hdr);
+		rc = decode_get_fru_record_table_req(request_msg, payload_len,
+				&data_transfer_handle, &transfer_operation_flag);
+		if (rc != PLDM_SUCCESS)
+			return rc;
+
+		rc = get_test_fru_table(&fru_table, &fru_table_len);
+		if (rc != OPAL_SUCCESS)
+			return rc;
+
+		payload_len =
+			(sizeof(struct pldm_get_fru_record_table_resp) - 1)
+			+ fru_table_len;
+
+		*response_len = sizeof(struct pldm_msg_hdr)
+			+ payload_len;
+
+		*response_msg = malloc(*response_len);
+		if (*response_msg == NULL) {
+			perror("PLDM_TEST malloc");
+			return OPAL_RESOURCE;
+		}
+
+		next_data_transfer_handle = PLDM_GET_NEXTPART;
+
+		if (fru_special_case ==
+				FRU_TABLE_TRANSFER_FLAG_ERROR)
+			transfer_operation_flag = PLDM_START;
+		else
+			transfer_operation_flag = PLDM_START_AND_END;
+
+		if (fru_special_case ==
+				FRU_TABLE_RESPONSE_ERROR)
+			completion_code = PLDM_START;
+
+		rc = encode_get_fru_record_table_resp(
+				((struct pldm_msg *)request_msg)->hdr.instance_id,
+				completion_code, next_data_transfer_handle,
+				transfer_operation_flag, *response_msg);
+		if (rc != PLDM_SUCCESS)
+			return OPAL_PARAMETER;
+
+		resp_payload = (struct pldm_get_fru_record_table_resp *)
+			((struct pldm_msg *)*response_msg)->payload;
+		memcpy(resp_payload->fru_record_table_data, fru_table, fru_table_len);
+		free(fru_table);
+
+		return OPAL_SUCCESS;
+
+	default:
+		return OPAL_PARAMETER;
+
+
+	}
+
+	return OPAL_SUCCESS;
+}
+
+
+int ast_mctp_message_tx(bool tag_owner __unused, uint8_t msg_tag __unused,
+		uint8_t *msg, int len)
+{
+	uint8_t *pldm_received_msg = msg+1;
+	void *response_msg;
+	size_t response_len;
+	int rc;
+
+	if (msg[0] != 0x01)
+		return OPAL_PARAMETER;
+
+	/* TEST Message TYPE: PLDM = 0x01 (000_0001b) as per MCTP - DSP0240 */
+	if (((struct pldm_msg *)pldm_received_msg)->hdr.request == PLDM_RESPONSE)
+		return OPAL_PARAMETER;
+
+	/* Reply to requests */
+	else if (((struct pldm_msg *)pldm_received_msg)->hdr.request == PLDM_REQUEST) {
+		rc = pldm_test_reply_request_fru(pldm_received_msg, len-1,
+				&response_msg, &response_len);
+		if (rc != OPAL_SUCCESS)
+			return rc;
+
+		if (response_len <= 0)
+			return OPAL_PARAMETER;
+
+		pldm_mctp_message_rx(BMC_EID, tag_owner,
+				msg_tag, response_msg,
+				response_len);
+		free(response_msg);
+	}
+	return OPAL_SUCCESS;
+
+}
+
+
+int test_pldm_fru_init_response_error(void)
+{
+	int rc;
+
+	fru_special_case = FRU_TABLE_RESPONSE_ERROR;
+
+	rc = pldm_fru_init();
+	if (rc  != OPAL_PARAMETER) {
+		printf("PLDM_TEST: %s failed :: rc = %d exp %d\n", __func__, rc, OPAL_PARAMETER);
+		fru_special_case = NORMAL_CASE;
+		return OPAL_PARAMETER;
+
+	}
+	fru_special_case = NORMAL_CASE;
+	return OPAL_SUCCESS;
+}
+
+
+int test_pldm_fru_init_transfer_flag_error(void)
+{
+	int rc;
+
+	fru_special_case = FRU_TABLE_TRANSFER_FLAG_ERROR;
+
+	rc = pldm_fru_init();
+	if (rc  != OPAL_PARAMETER) {
+		printf("PLDM_TEST: %s failed :: rc = %d exp %d\n", __func__, rc, OPAL_PARAMETER);
+		fru_special_case = NORMAL_CASE;
+		return OPAL_PARAMETER;
+
+	}
+	fru_special_case = NORMAL_CASE;
+	return OPAL_SUCCESS;
+}
+
+int test_pldm_fru_init(void)
+{
+	int rc;
+
+	fru_special_case = NORMAL_CASE;
+
+	rc = pldm_fru_init();
+	if (rc  != OPAL_SUCCESS) {
+		printf("PLDM_TEST: %s failed :: rc = %d exp %d\n", __func__, rc, OPAL_SUCCESS);
+		return OPAL_PARAMETER;
+
+	}
+	return OPAL_SUCCESS;
+}
+
+int test_pldm_local_table(void)
+{
+	int rc;
+	uint32_t table_length;
+	uint16_t total_record_set_identifiers;
+	uint16_t total_table_records;
+	uint8_t *fru_record_table;
+
+	fru_special_case = NORMAL_CASE;
+
+	pldm_fru_set_local_table(&table_length,
+			&total_record_set_identifiers,
+			&total_table_records);
+
+	rc = pldm_fru_get_local_table((void **)&fru_record_table,
+			&table_length);
+	if (rc  != OPAL_SUCCESS) {
+		printf("PLDM_TEST: %s failed :: rc = %d exp %d\n", __func__, rc, OPAL_SUCCESS);
+		return OPAL_PARAMETER;
+
+	}
+
+	return OPAL_SUCCESS;
+}
+
+
+struct test_case {
+	const char *name;
+	int (*fn)(void);
+};
+
+#define TEST_CASE(x) { #x, x }
+
+struct test_case test_cases[] = {
+	TEST_CASE(test_pldm_fru_init_response_error),
+	TEST_CASE(test_pldm_fru_init_transfer_flag_error),
+	TEST_CASE(test_pldm_fru_init),
+	TEST_CASE(test_pldm_local_table),
+	{NULL, NULL}
+};
+
+
+int main(void)
+{
+	struct test_case *tc = &test_cases[0];
+	int rc = 0;
+
+	pldm_requester_init();
+
+	do {
+		rc = tc->fn();
+		if (rc != OPAL_SUCCESS) {
+			printf("PLDM FILEIO TEST :%s FAILED\n", tc->name);
+			return -1;
+		}
+	} while ((++tc)->fn);
+	// This is to kill thread running to take requests
+	kill_poller();
+
+	return OPAL_SUCCESS;
+}
-- 
2.48.1



More information about the Skiboot mailing list