feat(doe): add PCIe DOE tests

This patch adds PCIe DOE tests for
- DOE discovery protocol
- SPDM get version
To build this test suite use 'TEST=pcie-doe'
option.

The spdm.h is imported from https://github.com/DMTF/libspdm
project.

Change-Id: I8db1048d01b4f8061d8a4ddccc198159ed61e6b7
Signed-off-by: AlexeiFedorov <Alexei.Fedorov@arm.com>
diff --git a/tftf/framework/framework.mk b/tftf/framework/framework.mk
index b25d1d0..0bae3cf 100644
--- a/tftf/framework/framework.mk
+++ b/tftf/framework/framework.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2018-2023, Arm Limited. All rights reserved.
+# Copyright (c) 2018-2024, Arm Limited. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -19,6 +19,8 @@
 	-Iinclude/lib					\
 	-Iinclude/lib/${ARCH}				\
 	-Iinclude/lib/extensions			\
+	-Iinclude/lib/pcie				\
+	-Iinclude/lib/spdm				\
 	-Iinclude/lib/utils				\
 	-Iinclude/lib/xlat_tables			\
 	-Iinclude/plat/common				\
diff --git a/tftf/tests/doe_tests/doe_helpers.c b/tftf/tests/doe_tests/doe_helpers.c
new file mode 100644
index 0000000..28b6a86
--- /dev/null
+++ b/tftf/tests/doe_tests/doe_helpers.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2024, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <debug.h>
+#include <pcie.h>
+#include <pcie_doe.h>
+#include <spdm.h>
+
+#include <platform_pcie.h>
+
+void pcie_init(void)
+{
+	/* Create PCIe table and enumeration */
+	pcie_create_info_table();
+}
+
+/*
+ * @brief  Returns the BDF Table pointer
+ *
+ * @param  None
+ *
+ * @return BDF Table pointer
+ */
+pcie_device_bdf_table_t *get_pcie_bdf_table(void)
+{
+	return pcie_get_bdf_table();
+}
+
+int find_doe_device(uint32_t *bdf_ptr, uint32_t *cap_base_ptr)
+{
+	pcie_device_bdf_table_t	*bdf_table_ptr = pcie_get_bdf_table();
+	uint32_t num_bdf = bdf_table_ptr->num_entries;
+
+	INFO("PCI BDF table entries: %u\n", num_bdf);
+
+	/* If no entries in BDF table return error */
+	if (num_bdf == 0) {
+		ERROR("No BDFs entries found\n");
+		return -ENODEV;
+	}
+
+	INFO("PCI BDF table 0x%lx\n", (uintptr_t)bdf_table_ptr);
+
+	while (num_bdf-- != 0) {
+		uint32_t bdf = bdf_table_ptr->device[num_bdf].bdf;
+		uint32_t status, doe_cap_base;
+
+		/* Check for DOE capability */
+		status = pcie_find_capability(bdf, PCIE_ECAP, DOE_CAP_ID, &doe_cap_base);
+		if (status == PCIE_SUCCESS) {
+			INFO("PCIe DOE capability: bdf 0x%x cap_base 0x%x\n", bdf, doe_cap_base);
+			*bdf_ptr = bdf;
+			*cap_base_ptr = doe_cap_base;
+			return 0;
+		}
+	}
+
+	ERROR("No PCIe DOE capability found\n");
+	return -ENODEV;
+}
+
+int get_spdm_version(uint32_t bdf, uint32_t doe_cap_base)
+{
+	uint32_t response[SPDM_GET_VERS_RESP_LEN], resp_len;
+	spdm_version_response_t *ver_resp;
+	spdm_version_number_t *ver_ptr;
+	uint8_t entry_count;
+	int ret;
+	const spdm_get_version_request_t ver_req = {
+		{ SPDM_MESSAGE_VERSION, SPDM_GET_VERSION, }
+	};
+
+	ret = pcie_doe_send_req(DOE_HEADER_1, bdf, doe_cap_base,
+				(uint32_t *)&ver_req,
+				sizeof(spdm_get_version_request_t));
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = pcie_doe_recv_resp(bdf, doe_cap_base, response, &resp_len);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ver_resp = (spdm_version_response_t *)response;
+
+	if (ver_resp->header.spdm_version != SPDM_MESSAGE_VERSION) {
+		ERROR("SPDM response v.%u doesn't match requested %u\n",
+			ver_resp->header.spdm_version,
+			SPDM_MESSAGE_VERSION);
+		return -EPROGMISMATCH;
+	}
+
+	if (ver_resp->header.request_response_code != SPDM_VERSION) {
+		ERROR("SPDM response code %u doesn't match expected %u\n",
+			ver_resp->header.request_response_code,
+			SPDM_VERSION);
+		return -EBADMSG;
+	}
+
+	entry_count = ver_resp->version_number_entry_count;
+	INFO("SPDM version entries: %u\n", entry_count);
+
+	ver_ptr = (spdm_version_number_t *)(
+			(uintptr_t)&ver_resp->version_number_entry_count +
+			sizeof(ver_resp->version_number_entry_count));
+
+	while (entry_count-- != 0) {
+		spdm_version_number_t ver = *ver_ptr++;
+
+		INFO("SPDM v%llu.%llu.%llu.%llu\n",
+			EXTRACT(SPDM_VER_MAJOR, ver),
+			EXTRACT(SPDM_VER_MINOR, ver),
+			EXTRACT(SPDM_VER_UPDATE, ver),
+			EXTRACT(SPDM_VER_ALPHA, ver));
+	}
+	return ret;
+}
+
+int doe_discovery(uint32_t bdf, uint32_t doe_cap_base)
+{
+	pcie_doe_disc_req_t request = { 0, };
+	pcie_doe_disc_resp_t response;
+	uint32_t resp_len;
+	int ret;
+
+	do {
+		ret = pcie_doe_send_req(DOE_HEADER_0, bdf, doe_cap_base,
+					(uint32_t *)&request,
+					sizeof(pcie_doe_disc_req_t));
+		if (ret != 0) {
+			return ret;
+		}
+
+		ret = pcie_doe_recv_resp(bdf, doe_cap_base,
+					(uint32_t *)&response, &resp_len);
+		if (ret != 0) {
+			return ret;
+		}
+
+		print_doe_disc(&response);
+		request.index = response.next_index;
+
+	} while (response.next_index != 0);
+
+	return 0;
+}
diff --git a/tftf/tests/doe_tests/doe_helpers.h b/tftf/tests/doe_tests/doe_helpers.h
new file mode 100644
index 0000000..08137e3
--- /dev/null
+++ b/tftf/tests/doe_tests/doe_helpers.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2024, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef DOE_HELPERS_H
+#define DOE_HELPERS_H
+
+void pcie_init(void);
+int find_doe_device(uint32_t *bdf_ptr, uint32_t *cap_base_ptr);
+int doe_discovery(uint32_t bdf, uint32_t doe_cap_base);
+int get_spdm_version(uint32_t bdf, uint32_t doe_cap_base);
+
+#endif /* DOE_HELPERS_H */
diff --git a/tftf/tests/doe_tests/test_doe.c b/tftf/tests/doe_tests/test_doe.c
new file mode 100644
index 0000000..6eb5079
--- /dev/null
+++ b/tftf/tests/doe_tests/test_doe.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2024, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "doe_helpers.h"
+
+#include <test_helpers.h>
+
+#define SKIP_TEST_IF_DOE_NOT_SUPPORTED()					\
+	do {									\
+		if (find_doe_device(&bdf, &doe_cap_base) != 0) {		\
+			tftf_testcase_printf("PCIe DOE not supported\n");	\
+			return TEST_RESULT_SKIPPED;				\
+		}								\
+	} while (false)
+
+test_result_t doe_discovery_test(void)
+{
+	uint32_t bdf, doe_cap_base;
+	int ret;
+
+	pcie_init();
+
+	SKIP_TEST_IF_DOE_NOT_SUPPORTED();
+
+	ret = doe_discovery(bdf, doe_cap_base);
+	if (ret != 0) {
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
+
+test_result_t spdm_version_test(void)
+{
+	uint32_t bdf, doe_cap_base;
+	int ret;
+
+	SKIP_TEST_IF_DOE_NOT_SUPPORTED();
+
+	ret = get_spdm_version(bdf, doe_cap_base);
+	if (ret != 0) {
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/tests-pcie-doe.mk b/tftf/tests/tests-pcie-doe.mk
new file mode 100644
index 0000000..14bc9cf
--- /dev/null
+++ b/tftf/tests/tests-pcie-doe.mk
@@ -0,0 +1,22 @@
+#
+# Copyright (c) 2024, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+TESTS_SOURCES	+=			\
+	$(addprefix plat/arm/fvp/,	\
+		fvp_pcie.c		\
+	)
+
+TESTS_SOURCES	+=				\
+	$(addprefix tftf/tests/doe_tests/,	\
+		doe_helpers.c			\
+		test_doe.c			\
+	)
+
+TESTS_SOURCES	+=		\
+	$(addprefix lib/pcie/,	\
+		pcie.c		\
+		pcie_doe.c	\
+	)
diff --git a/tftf/tests/tests-pcie-doe.xml b/tftf/tests/tests-pcie-doe.xml
new file mode 100644
index 0000000..ed8b7cb
--- /dev/null
+++ b/tftf/tests/tests-pcie-doe.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright (c) 2024, Arm Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-3-Clause
+-->
+
+<testsuites>
+  <testsuite name="PCI Data Object Exchange" description="Check PCI DOE support">
+     <testcase name="PCI DOE Discovery" function="doe_discovery_test" />
+     <testcase name="SPDM Get Version" function="spdm_version_test" />
+  </testsuite>
+</testsuites>