feat(lfa): add LFA SMCs tests using a single CPU

Add a LFA tests for single CPU without the Activate SMC.
Also, include a negative test case where the firmware
is unavailable for activation during the Prime phase,
resulting in a failure.

Signed-off-by: Manish V Badarkhe <Manish.Badarkhe@arm.com>
Change-Id: I9fff281f77de87580f5b3c1fecd69fbf0f5d1f2d
diff --git a/include/runtime_services/lfa.h b/include/runtime_services/lfa.h
new file mode 100644
index 0000000..09d397d
--- /dev/null
+++ b/include/runtime_services/lfa.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef LFA_H
+#define LFA_H
+
+#include <stdint.h>
+
+#include <arch_helpers.h>
+#include <arm_arch_svc.h>
+#include <smccc.h>
+
+#define LFA_VERSION_MAJOR		U(1)
+#define LFA_VERSION_MINOR		U(0)
+#define LFA_VERSION_MAJOR_SHIFT		U(16)
+#define LFA_VERSION_MAJOR_MASK 		U(0x7FFF)
+#define LFA_VERSION_MINOR_SHIFT		U(0)
+#define LFA_VERSION_MINOR_MASK		U(0xFFFF)
+
+#define LFA_VERSION			U(0xC40002E0)
+#define LFA_FEATURES			U(0xC40002E1)
+#define LFA_GET_INFO			U(0xC40002E2)
+#define LFA_GET_INVENTORY		U(0xC40002E3)
+#define LFA_PRIME			U(0xC40002E4)
+#define LFA_ACTIVATE			U(0xC40002E5)
+#define LFA_CANCEL			U(0xC40002E6)
+#define LFA_INVALID			LFA_CANCEL + 1U
+
+#endif /* LFA_H */
diff --git a/tftf/tests/runtime_services/lfa/test_lfa_single_cpu.c b/tftf/tests/runtime_services/lfa/test_lfa_single_cpu.c
new file mode 100644
index 0000000..796f3e9
--- /dev/null
+++ b/tftf/tests/runtime_services/lfa/test_lfa_single_cpu.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <debug.h>
+#include <lfa.h>
+#include <test_helpers.h>
+#include <tftf_lib.h>
+
+#define LFA_GET_INVENTORY_RESP_X1	UL(0x4698fe4c6d08d447)
+#define LFA_GET_INVENTORY_RESP_X2	UL(0x005abdcb5029959b)
+
+static uint64_t num_components;
+static uint64_t fw_id;
+
+/*
+ * @Test_Aim@ Simple surface tests for Live Firmware Activation.
+ *
+ * This test checks the version number. It runs on the lead CPU.
+ */
+test_result_t test_lfa_version(void)
+{
+	smc_args args = { LFA_VERSION };
+	uint32_t major, minor;
+	smc_ret_values ret = tftf_smc(&args);
+
+	major = (uint32_t)((ret.ret0 >> LFA_VERSION_MAJOR_SHIFT) & LFA_VERSION_MAJOR_MASK);
+	minor = (uint32_t)((ret.ret0 >> LFA_VERSION_MINOR_SHIFT) & LFA_VERSION_MINOR_MASK);
+
+	VERBOSE("%s LFA API Version : %d.%d\n", __func__, major, minor);
+
+	if ((major != LFA_VERSION_MAJOR) || (minor != LFA_VERSION_MINOR)) {
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * @Test_Aim@ Test for LFA_FEATURES.
+ *
+ * This test checks LFA_FEATURES for the two extreme FID values and then does
+ * one negative test.
+ */
+test_result_t test_lfa_features(void)
+{
+	smc_args args = { .fid = LFA_FEATURES, .arg1 = LFA_VERSION };
+	smc_ret_values ret = tftf_smc(&args);
+
+	if (ret.ret0 != SMC_OK) {
+		tftf_testcase_printf("%s: Features for FID=0x%08lx returned %08lx\n",
+				     __func__, args.arg1, ret.ret0);
+		return TEST_RESULT_FAIL;
+	}
+
+	args.arg1 = LFA_CANCEL;
+	ret = tftf_smc(&args);
+	if (ret.ret0 != SMC_OK) {
+		tftf_testcase_printf("%s: Features for FID=0x%08lx returned %08lx\n",
+				     __func__, args.arg1, ret.ret0);
+		return TEST_RESULT_FAIL;
+	}
+
+	args.arg1 = LFA_INVALID;
+	ret = tftf_smc(&args);
+	if (ret.ret0 == SMC_OK) {
+		tftf_testcase_printf("%s: Features for FID=0x%08lx returned %08lx\n",
+				     __func__, args.arg1, ret.ret0);
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * @Test_Aim@ Test for LFA_GET_INFO.
+ *
+ * This test checks that LFA_GET_INFO returns successfully and the proper
+ * error if an invalid lfa_selector is provided.
+ */
+test_result_t test_lfa_get_info(void)
+{
+	smc_args args = { .fid = LFA_GET_INFO, .arg1 = 0U };
+	smc_ret_values ret = tftf_smc(&args);
+
+	if (ret.ret0 != SMC_OK) {
+		tftf_testcase_printf("%s: Unexpected error: 0x%08lx\n",
+				     __func__, ret.ret0);
+		return TEST_RESULT_FAIL;
+	}
+
+	num_components = ret.ret1;
+
+	/* Try giving invalid argument and expect Failure from SMC */
+	args.arg1 = 1U;
+	ret = tftf_smc(&args);
+	if (ret.ret0 == SMC_OK) {
+		tftf_testcase_printf("%s: Unexpected success\n", __func__);
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * @Test_Aim@ Test for LFA_GET_INVENTORY.
+ */
+test_result_t test_lfa_get_inventory(void)
+{
+	smc_args args = { .fid = LFA_GET_INVENTORY, .arg1 = 0U };
+	smc_ret_values ret;
+
+	for (uint64_t i = 0U; i < num_components; i++) {
+		args.arg1 = i;
+
+		ret = tftf_smc(&args);
+		if (ret.ret0 != SMC_OK) {
+			tftf_testcase_printf("%s: Unexpected error: 0x%08lx\n",
+					     __func__, ret.ret0);
+			return TEST_RESULT_FAIL;
+		}
+
+		if ((ret.ret1 == LFA_GET_INVENTORY_RESP_X1) &&
+		    (ret.ret2 == LFA_GET_INVENTORY_RESP_X2)) {
+			fw_id = i;
+		}
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * @Test_Aim@ Test for LFA_PRIME.
+ */
+test_result_t test_lfa_prime(void)
+{
+	smc_args args = { .fid = LFA_PRIME, .arg1 = fw_id };
+	smc_ret_values ret = tftf_smc(&args);
+
+	if (ret.ret0 == SMC_OK) {
+		/*
+		 * TODO: currently expected to be failed as BL31 prime
+		 * is not present. This is added to exercise negative
+		 * scenario.
+		 */
+		tftf_testcase_printf("%s: Unexpected error: 0x%08lx\n",
+				     __func__, ret.ret0);
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * @Test_Aim@ Test for LFA_ACTIVATE.
+ */
+test_result_t test_lfa_activate(void)
+{
+	smc_args args = { .fid = LFA_ACTIVATE, .arg1 = fw_id };
+	smc_ret_values ret = tftf_smc(&args);
+
+	if (ret.ret0 == SMC_OK) {
+		/*
+		 * TODO: currently expected to be failed as BL31 activate
+		 * is not present. This is added to exercise negative
+		 * scenario.
+		 */
+		tftf_testcase_printf("%s: Unexpected error: 0x%08lx\n",
+				     __func__, ret.ret0);
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * @Test_Aim@ Test for LFA_CANCEL.
+ */
+test_result_t test_lfa_cancel(void)
+{
+	smc_args args = { .fid = LFA_CANCEL, .arg1 = fw_id };
+	smc_ret_values ret;
+
+	ret = tftf_smc(&args);
+	/*
+	 * TODO: currently expected to be failed as BL31 cancel
+	 * is not present. This is added to exercise negative
+	 * scenario.
+	 */
+	if (ret.ret0 == SMC_OK) {
+		tftf_testcase_printf("%s: Unexpected error: 0x%08lx\n",
+				     __func__, ret.ret0);
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/tests-lfa.mk b/tftf/tests/tests-lfa.mk
new file mode 100644
index 0000000..a66dd53
--- /dev/null
+++ b/tftf/tests/tests-lfa.mk
@@ -0,0 +1,9 @@
+#
+# Copyright (c) 2025, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+TESTS_SOURCES	+=	$(addprefix tftf/tests/runtime_services/lfa/,	\
+			test_lfa_single_cpu.c				\
+)
diff --git a/tftf/tests/tests-lfa.xml b/tftf/tests/tests-lfa.xml
new file mode 100644
index 0000000..77222c7
--- /dev/null
+++ b/tftf/tests/tests-lfa.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright (c) 2025, Arm Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-3-Clause
+-->
+
+<testsuites>
+  <testsuite name="lfa" description="Live Firmware Activation">
+     <testcase name="Query version" function="test_lfa_version" />
+     <testcase name="Check features" function="test_lfa_features" />
+     <testcase name="Get Info" function="test_lfa_get_info" />
+     <testcase name="Get Inventory" function="test_lfa_get_inventory" />
+     <testcase name="Prime" function="test_lfa_prime" />
+     <testcase name="Activate" function="test_lfa_activate" />
+     <testcase name="Cancel" function="test_lfa_cancel" />
+  </testsuite>
+</testsuites>