[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