[PATCH 1/1] smccc: Test for implementation of ARM_SMCCC functions

G Edhaya Chandran edhaya.chandran at arm.com
Tue Jul 15 19:36:37 UTC 2025


New tests added to check for
ARM_SMCCC_VERSION
ARM_SMCCC_ARCH_FEATURES
ARM_SMCCC_TEST_ARCH_SOC_ID
 with SoC_ID_type = 1 and SoC_ID_type = 2.

The tests will invoke SMCCC functions,
recieve the results and interpret and print
according to the
Arm SMC Calling Convention specification: https://developer.arm.com/documentation/den0028/latest/


Also Assertions are added according to
Arm BBR v2.2 Specifications: https://developer.arm.com/documentation/den0044/latest/
 Section 4.1. SMCCC architecture call requirements
These will be active only on running the FWTS
with the --sbbr or the --ebbr flag

Signed-off-by: G Edhaya Chandran <edhaya.chandran at arm.com>
---
 smccc_test/smccc_test.c |  58 +++++++
 smccc_test/smccc_test.h |   7 +-
 src/pci/smccc/smccc.c   | 360 +++++++++++++++++++++++++++++++++++++++-
 3 files changed, 423 insertions(+), 2 deletions(-)

diff --git a/smccc_test/smccc_test.c b/smccc_test/smccc_test.c
index 9156a81008a8..b37691898852 100644
--- a/smccc_test/smccc_test.c
+++ b/smccc_test/smccc_test.c
@@ -73,6 +73,18 @@ MODULE_LICENSE("GPL");
 #define SMCCC_PCI_SEG_INFO	SMCCC_PCI_CALL_VAL(0x134)
 #endif
 
+#ifndef SMCCC_VERSION
+#define SMCCC_VERSION	0x80000000
+#endif
+
+#ifndef SMCCC_ARCH_FEATURES
+#define SMCCC_ARCH_FEATURES	0x80000001
+#endif
+
+#ifndef SMCCC_ARCH_SOC_ID
+#define SMCCC_ARCH_SOC_ID	0x80000002
+#endif
+
 /*
  *  smccc_test_copy_to_user()
  *	copy arm_res a* registers to user space test_arg w array
@@ -147,6 +159,46 @@ static long smccc_test_pci_get_seg_info(unsigned long arg)
 	return smccc_test_copy_to_user(arg, &arm_res, conduit);
 }
 
+static long smccc_test_version(unsigned long arg)
+{
+	struct arm_smccc_res arm_res = { };
+	int conduit;
+
+	conduit = arm_smccc_1_1_invoke(SMCCC_VERSION, 0, 0, 0, 0, 0, 0, 0, &arm_res);
+
+	return smccc_test_copy_to_user(arg, &arm_res, conduit);
+}
+
+static long smccc_test_arch_features(unsigned long arg)
+{
+	struct arm_smccc_res arm_res = { };
+	struct smccc_test_arg test_arg;
+	int ret, conduit;
+
+	ret = smccc_test_copy_from_user(&test_arg, arg);
+	if (ret)
+		return ret;
+
+	conduit = arm_smccc_1_1_invoke(SMCCC_ARCH_FEATURES, test_arg.w[1], 0, 0, 0, 0, 0, 0, &arm_res);
+
+	return smccc_test_copy_to_user(arg, &arm_res, conduit);
+}
+
+static long smccc_test_arch_soc_id(unsigned long arg)
+{
+	struct arm_smccc_res arm_res = { };
+	struct smccc_test_arg test_arg;
+	int ret, conduit;
+
+	ret = smccc_test_copy_from_user(&test_arg, arg);
+	if (ret)
+		return ret;
+
+	conduit = arm_smccc_1_1_invoke(SMCCC_ARCH_SOC_ID, test_arg.w[1], 0, 0, 0, 0, 0, 0, &arm_res);
+
+	return smccc_test_copy_to_user(arg, &arm_res, conduit);
+}
+
 static long smccc_test_ioctl(struct file *file, unsigned int cmd,
 			     unsigned long arg)
 {
@@ -171,6 +223,12 @@ static long smccc_test_ioctl(struct file *file, unsigned int cmd,
 		return -ENOTSUPP;
 	case SMCCC_TEST_PCI_GET_SEG_INFO:
 		return smccc_test_pci_get_seg_info(arg);
+	case SMCCC_TEST_VERSION_FUNCTION:
+		return smccc_test_version(arg);
+	case SMCCC_TEST_ARCH_FEATURES:
+		return smccc_test_arch_features(arg);
+	case SMCCC_TEST_ARCH_SOC_ID:
+		return smccc_test_arch_soc_id(arg);
 	default:
 		break;
 	}
diff --git a/smccc_test/smccc_test.h b/smccc_test/smccc_test.h
index 39a2331e4820..6597d87e7b20 100644
--- a/smccc_test/smccc_test.h
+++ b/smccc_test/smccc_test.h
@@ -39,5 +39,10 @@ struct smccc_test_arg {
         _IOWR('p', 0x04, struct smccc_test_arg)
 #define SMCCC_TEST_PCI_GET_SEG_INFO \
         _IOWR('p', 0x05, struct smccc_test_arg)
-
+#define SMCCC_TEST_VERSION_FUNCTION \
+        _IOWR('p', 0x06, struct smccc_test_arg)
+#define SMCCC_TEST_ARCH_FEATURES \
+        _IOWR('p', 0x07, struct smccc_test_arg)
+#define SMCCC_TEST_ARCH_SOC_ID \
+        _IOWR('p', 0x08, struct smccc_test_arg)
 #endif
diff --git a/src/pci/smccc/smccc.c b/src/pci/smccc/smccc.c
index 76261228bb2e..5574d01502f5 100644
--- a/src/pci/smccc/smccc.c
+++ b/src/pci/smccc/smccc.c
@@ -51,6 +51,21 @@ enum {
 #define PCI_WRITE	 (0x84000133)
 #define PCI_GET_SEG_INFO (0x84000134)
 
+#define ARM_SMCCC_VERSION       	  0x80000000
+#define ARM_SMCCC_ARCH_FEATURES 	  0x80000001
+#define ARM_SMCCC_ARCH_SOC_ID    	  0x80000002
+#define ARM_SMCCC_ARCH_SOC_ID_SMC64   0xC0000002
+
+#define SOC_ID_TYPE_VERSION  0x0
+#define SOC_ID_TYPE_REVISION 0x1
+#define SOC_ID_TYPE_SOC_NAME 0x2
+
+#define SMCCC_VERSION_1_1 0x10001
+#define SMCCC_VERSION_1_2 0x10002
+
+#define ARM_SMCCC_RET_NOT_SUPPORTED -1
+#define ARM_SMCCC_SUPPORTED 		 0
+
 /* SMCCC API id to name mapping */
 typedef struct {
 	const uint32_t	pci_func_id;
@@ -71,6 +86,23 @@ static const char *dev_name = "/dev/smccc_test";
 static bool module_loaded;
 static int smccc_fd = -1;
 
+typedef struct {
+	const uint32_t	SMCCC_func_id;
+	const char	*SMCCC_func_id_name;
+	bool		implemented;
+} SMCCC_func_id_t;
+
+
+static SMCCC_func_id_t SMCCC_func_id_list[] = {
+	{ ARM_SMCCC_VERSION,	   	   "ARM_SMCCC_VERSION",			  false },
+	{ ARM_SMCCC_ARCH_FEATURES, 	   "ARM_SMCCC_ARCH_FEATURES",	  false },
+	{ ARM_SMCCC_ARCH_SOC_ID,	   "ARM_SMCCC_ARCH_SOC_ID",		  false },
+	{ ARM_SMCCC_ARCH_SOC_ID_SMC64, "ARM_SMCCC_ARCH_SOC_ID_SMC64", false }
+};
+
+static uint16_t smccc_major_version = 0;
+static uint16_t smccc_minor_version = 0;
+
 static int smccc_init(fwts_framework *fw)
 {
 	if (fwts_module_load(fw, module_name) != FWTS_OK) {
@@ -193,6 +225,328 @@ static int smccc_pci_version_test(fwts_framework *fw)
 	return FWTS_OK;
 }
 
+/*
+ *  smccc_version_test()
+ *	test SMCCC function VERSION
+ */
+static int smccc_version_test(fwts_framework *fw)
+{
+	int ret;
+	struct smccc_test_arg arg = { };
+
+	arg.size = sizeof(arg);
+	arg.w[0] = ARM_SMCCC_VERSION;
+
+	ret = ioctl(smccc_fd, SMCCC_TEST_VERSION_FUNCTION, &arg);
+	if (ret < 0) {
+		fwts_log_error(fw, "SMCCC test driver ioctl SMCCC_TEST_VERSION "
+			"failed, errno=%d (%s)\n", errno, strerror(errno));
+		return FWTS_ERROR;
+	}
+	if (smccc_pci_conduit_check(fw, &arg) != FWTS_OK)
+		return FWTS_ERROR;
+
+	fwts_log_info_verbatim(fw, "  SMCCC conduit type: 0x%x ('%s')",
+		arg.conduit, smccc_pci_conduit_name(&arg));
+
+	int32_t return_value = arg.w[0];
+	
+	/* NOT_SUPPORTED is treated as v1.0 */
+	if(return_value == ARM_SMCCC_RET_NOT_SUPPORTED)
+	{
+		smccc_major_version = 1;
+		smccc_minor_version = 0;
+		fwts_log_info_verbatim(fw, "Arm SMCCC: Major version: %d, Minor version: %d",
+			smccc_major_version, smccc_minor_version);
+	}
+	else
+	{
+		/* Bit 31 should not be 1. Bits 30...0 should be non 0 */
+		if((arg.w[0] >> 31) & 0x1 || ((arg.w[0] & 0x7FFFFFFF) == 0))
+		{
+			fwts_log_error(fw, "SMCCC_TEST_VERSION return value is invalid." 
+				"arg.w[0] = 0x%8.8" PRIx32,
+				arg.w[0]);
+			return FWTS_ERROR;
+		}
+
+		smccc_major_version = arg.w[0] >> 16;
+		smccc_minor_version = arg.w[0] & 0x0000ffff;
+		fwts_log_info_verbatim(fw, "Arm SMCCC: Major version: %d, Minor version: %d",
+			smccc_major_version, smccc_minor_version);
+
+	}
+
+	fwts_passed(fw, "Arm SMCCC_VERSION test passed");
+
+	return FWTS_OK;
+}
+
+/*
+ * smccc_arch_features_bbr_check()
+ * Asserts based on the Arm BBR specification rules in
+ * Section 4.1. SMCCC architecture call requirements 
+ * This function can be run only if SBBR or EBBR flag is set
+ * while running FWTS
+ */
+
+ static int smccc_arch_features_bbr_check(fwts_framework *fw)
+ {
+	uint32_t version = (smccc_major_version << 16) + smccc_minor_version;
+	bool bbr_check_failed = false; 
+	uint16_t assert_failed_count = 0;
+	int i=0;
+
+	fwts_log_info_verbatim(fw, "SMCCC Arm BBR check - the detected SMCCC version is %d.%d",
+		smccc_major_version, smccc_minor_version);
+
+	for (i=0; i<FWTS_ARRAY_SIZE(SMCCC_func_id_list); i++)
+	{
+		if(SMCCC_func_id_list[i].implemented == false)
+		{
+			switch (SMCCC_func_id_list[i].SMCCC_func_id)
+			{
+				case ARM_SMCCC_VERSION:
+				case ARM_SMCCC_ARCH_FEATURES:
+				{
+					if(version >= SMCCC_VERSION_1_1)
+					{
+						bbr_check_failed = true;
+					}
+				}
+				break; 
+				
+				case ARM_SMCCC_ARCH_SOC_ID:
+				{
+					if(version >= SMCCC_VERSION_1_2)
+					{
+						bbr_check_failed = true;
+					}
+				}
+				break;
+
+				default:
+				{
+					fwts_log_info_verbatim(fw, "Info: Function_id: 0x%8.8" PRIx32 " (%s) is not Implemented."
+						" However this function is not \"Required\" for this version of SMCCC as per the Arm BBR specification",
+						SMCCC_func_id_list[i].SMCCC_func_id,
+						SMCCC_func_id_list[i].SMCCC_func_id_name);
+				}
+			}
+			if(bbr_check_failed == true)
+			{
+				fwts_log_error(fw, "failed: As per Arm BBR specifciation, "
+					"the implementation of %s is required in SMCCC v%d.%d"
+					, SMCCC_func_id_list[i].SMCCC_func_id_name
+					, smccc_major_version, smccc_minor_version);
+				bbr_check_failed = false;
+
+				assert_failed_count++;
+			}
+		}
+	}
+
+	if (assert_failed_count > 0)
+	{
+		fwts_log_error(fw, "Arm BBR check for ARM_SMCCC_ARCH_FEATURES failed");
+		return FWTS_ERROR;
+	}
+
+	fwts_log_info_verbatim(fw, "Arm BBR check for ARM_SMCCC_ARCH_FEATURES passed");
+	return FWTS_OK;
+ }
+
+
+/*
+ *  smccc_arch_soc_features()
+ *	test SMCCC function ARCH_FEATURES
+ */
+
+ static int smccc_arch_features(fwts_framework *fw)
+ {
+	int ret;
+	int i=0;
+	struct smccc_test_arg arg = { };
+	
+
+	for (i=0; i<FWTS_ARRAY_SIZE(SMCCC_func_id_list); i++)
+	{
+		memset(&arg, 0, sizeof(arg));
+		arg.size = sizeof(arg);
+		arg.w[0] = ARM_SMCCC_ARCH_FEATURES;
+		
+		/* arg.w[1] should contain, the function id of an 
+		 * Arm Architecture Service Function to query about */
+		arg.w[1] = SMCCC_func_id_list[i].SMCCC_func_id;
+	
+		ret = ioctl(smccc_fd, SMCCC_TEST_ARCH_FEATURES, &arg);
+		if (ret < 0) {
+			fwts_log_error(fw, "SMCCC test driver ioctl SMCCC_TEST_ARCH_FEATURES "
+				"failed, errno=%d (%s)\n", errno, strerror(errno));
+			return FWTS_ERROR;
+		}
+		if (smccc_pci_conduit_check(fw, &arg) != FWTS_OK)
+			return FWTS_ERROR;
+	
+		int32_t return_value = arg.w[0];
+		
+		if (return_value < 0)
+		{
+			fwts_log_info_verbatim(fw, "   Features query for function_id: 0x%8.8" PRIx32
+				" for %s"
+				" returned: \"NOT IMPLEMENTED\" (%d)"
+				"\n      Function 0x%8.8" PRIx32 " is not implemented or arch_func_id is not in Arm Architecture Service range.",
+				SMCCC_func_id_list[i].SMCCC_func_id, 
+				SMCCC_func_id_list[i].SMCCC_func_id_name,
+				return_value,
+			    SMCCC_func_id_list[i].SMCCC_func_id);
+			SMCCC_func_id_list[i].implemented = false;
+		}
+		else if (return_value == ARM_SMCCC_SUPPORTED)
+		{
+			fwts_log_info_verbatim(fw, "   Features query for function_id: 0x%8.8" PRIx32
+				" for %s"
+				" returned: \"SUPPORTED\" (%d)",
+				SMCCC_func_id_list[i].SMCCC_func_id,
+				SMCCC_func_id_list[i].SMCCC_func_id_name,
+				return_value);
+			SMCCC_func_id_list[i].implemented = true;
+		}
+		else
+		{
+			/* return_value > 0 */
+			fwts_log_error(fw, "   Features query for function_id: 0x%8.8" PRIx32
+				" for %s"
+				" returned: \"OPTIONAL FUNCTION IMPLEMENTED\" (%d)"
+				"\n      Function 0x%8.8" PRIx32 " may support additional capabilities specific to the function ID. ",
+				SMCCC_func_id_list[i].SMCCC_func_id, 
+				SMCCC_func_id_list[i].SMCCC_func_id_name,
+				return_value,
+			    SMCCC_func_id_list[i].SMCCC_func_id);
+			SMCCC_func_id_list[i].implemented = true;
+		}
+	}
+
+	/* if --sbbr or --ebbr flag is set, additionally call the Arm BBR assertions function */
+	if (fw->flags & FWTS_FLAG_SBBR || fw->flags & FWTS_FLAG_EBBR)
+	{
+		if (smccc_arch_features_bbr_check(fw) == FWTS_ERROR)
+		{
+			fwts_failed(fw, LOG_LEVEL_HIGH, "smccc_arch_features check ",
+		    		"for Arm BBR specification rules failed\n");
+			return (FWTS_ERROR);
+		}
+	}
+
+	fwts_passed(fw, "Arm SMCCC_TEST_ARCH_FEATURES test passed");
+	return FWTS_OK;
+ }
+
+/*
+ *  smccc_arch_soc_id_test()
+ *	test SMCCC function ARCH_SOC_ID for 
+ *  SoC_ID_Type = 0 (Version)
+ */
+
+static int smccc_arch_soc_id_test_type0(fwts_framework *fw)
+{
+	int ret;
+	struct smccc_test_arg arg = { };
+
+	arg.size = sizeof(arg);
+	arg.w[0] = ARM_SMCCC_ARCH_SOC_ID;
+	arg.w[1] = SOC_ID_TYPE_VERSION;
+
+	ret = ioctl(smccc_fd, SMCCC_TEST_ARCH_SOC_ID, &arg);
+	if (ret < 0) {
+		fwts_log_error(fw, "SMCCC test driver ioctl SMCCC_TEST_ARCH_SOC_ID "
+			"failed, errno=%d (%s)\n", errno, strerror(errno));
+		return FWTS_ERROR;
+	}
+	if (smccc_pci_conduit_check(fw, &arg) != FWTS_OK)
+		return FWTS_ERROR;
+
+	fwts_log_info_verbatim(fw, "  SMCCC conduit type: 0x%x ('%s')",
+		arg.conduit, smccc_pci_conduit_name(&arg));
+
+	/* Bit 31 should not be 1. Bits 30...0 should be non 0 */
+	if((arg.w[0] >> 31) & 0x1 || ((arg.w[0] & 0x7FFFFFFF) == 0))
+	{
+		fwts_log_error(fw, "SMCCC_TEST_ARCH_SOC_ID return value is invalid." 
+			"arg.w[0] = 0x%8.8" PRIx32,
+			arg.w[0]);
+		fwts_failed(fw, LOG_LEVEL_HIGH, "Arm SMCCC_ARCH_SOC_ID test for ",
+				" SoC_ID_Type = 0 failed\n");
+		return FWTS_ERROR;
+	}
+
+	uint32_t SoC_ID = arg.w[0] & 0x0000ffff;
+	uint32_t JEP_106_Id_Code = (arg.w[0] >> 16) & 0x000000ff;
+	uint32_t JEP_106_Bank_Index = (arg.w[0] >> 24) & 0x000000ff;
+
+	fwts_log_info_verbatim(fw, "  SMCCC_TEST_ARCH_SOC_ID result for SoC_ID_type = %" PRIx16 
+		"\n   JEP-106 identification bank index for the SiP : 0x%2.2" PRIx16 
+		"\n   JEP-106 identification code with parity bit for the SiP : 0x%2.2" PRIx16
+		"\n   Implementation defined SoC ID : 0x%4.4" PRIx16
+		"\n   Info: arg.w[0] full result : 0x%8.8" PRIx32 ,
+		SOC_ID_TYPE_VERSION, SoC_ID, JEP_106_Id_Code, JEP_106_Bank_Index,  arg.w[0]);
+
+	fwts_passed(fw, "Arm SMCCC_ARCH_SOC_ID test for SoC_ID_Type = 0 passed");
+
+	return FWTS_OK;
+}
+
+
+/*
+ *  smccc_arch_soc_id_test()
+ *	test SMCCC function ARCH_SOC_ID for
+ *  SoC_ID_Type = 1 (Revision)
+ */
+
+static int smccc_arch_soc_id_test_type1(fwts_framework *fw)
+{
+	int ret;
+	struct smccc_test_arg arg = { };
+
+	arg.size = sizeof(arg);
+	arg.w[0] = ARM_SMCCC_ARCH_SOC_ID;
+	arg.w[1] = SOC_ID_TYPE_REVISION;
+
+	ret = ioctl(smccc_fd, SMCCC_TEST_ARCH_SOC_ID, &arg);
+	if (ret < 0) {
+		fwts_log_error(fw, "SMCCC test driver ioctl SMCCC_TEST_ARCH_SOC_ID "
+			"failed, errno=%d (%s)\n", errno, strerror(errno));
+		return FWTS_ERROR;
+	}
+	if (smccc_pci_conduit_check(fw, &arg) != FWTS_OK)
+		return FWTS_ERROR;
+
+	/* Bit 31 should not be 1. Bits 30...0 should be non 0 */
+	if((arg.w[0] >> 31) & 0x1 || ((arg.w[0] & 0x7FFFFFFF) == 0))
+	{
+		fwts_log_error(fw, "SMCCC_TEST_ARCH_SOC_ID return value is invalid." 
+			"arg.w[0] = 0x%8.8" PRIx16,
+			arg.w[0]);
+		fwts_failed(fw, LOG_LEVEL_HIGH, "Arm SMCCC_ARCH_SOC_ID test for ",
+				" SoC_ID_Type = 1 failed\n");
+		return FWTS_ERROR;
+	}
+
+	fwts_log_info_verbatim(fw, "  SMCCC conduit type: 0x%x ('%s')",
+		arg.conduit, smccc_pci_conduit_name(&arg));
+
+	uint32_t SoC_revision = arg.w[0] & 0x7fffffff;
+
+	fwts_log_info_verbatim(fw, "  SMCCC_TEST_ARCH_SOC_ID result for SoC_ID_type = %" PRIx16 
+		"\n   SoC Revision: 0x%8.8" PRIx32
+		"\n   Info: arg.w[0] full result : 0x%8.8" PRIx32,
+		SOC_ID_TYPE_REVISION, SoC_revision, arg.w[0]);
+
+	fwts_passed(fw, "Arm SMCCC_ARCH_SOC_ID test for SoC_ID_Type = 1 passed");
+
+	return FWTS_OK;
+}
+
 /*
  *  smccc_pci_features_test()
  *	test SMCCC function PCI_FEATURES
@@ -322,11 +676,15 @@ static fwts_framework_minor_test smccc_tests[] = {
 	{ smccc_pci_version_test,	"Test PCI_VERSION" },
 	{ smccc_pci_features_test,	"Test PCI_FEATURES" },
 	{ smccc_pci_get_seg_info,	"Test PCI_GET_SEG_INFO" },
+	{ smccc_version_test,	        "Test ARM_SMCCC_VERSION " },
+	{ smccc_arch_features,	        "Test ARM_SMCCC_ARCH_FEATURES " },
+	{ smccc_arch_soc_id_test_type0,	"Test ARM_SMCCC_ARCH_SOC_ID for Soc_ID_type 0 " },
+	{ smccc_arch_soc_id_test_type1,	"Test ARM_SMCCC_ARCH_SOC_ID for Soc_ID_type 1 " },
 	{ NULL, NULL }
 };
 
 static fwts_framework_ops smcccops = {
-	.description = "ARM64 PCI SMMCCC tests.",
+	.description = "ARM64 SMCCC tests.",
 	.init        = smccc_init,
 	.deinit      = smccc_deinit,
 	.minor_tests = smccc_tests
-- 
2.34.1




More information about the fwts-devel mailing list