[PATCH v2 1/1] smccc: Test for implementation of ARM_SMCCC functions
G Edhaya Chandran
edhaya.chandran at arm.com
Mon Jul 21 20:01:01 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
Also Assertions are added according to
Arm BBR v2.2 Specifications
Section 4.1. SMCCC architecture call requirements
These will be active only on running the FWTS
with the --sbbr or the --ebbr flag
Changes since v1:
Corrected the logs in smccc_arch_soc_id_test_type0()
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..144a0a24839b 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, JEP_106_Bank_Index, JEP_106_Id_Code, SoC_ID, 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