| Message ID | 20250915191415.3710447-1-edhaya.chandran@arm.com |
|---|---|
| State | New |
| Headers | show |
| Series | [1/1] pci/smccc: New tests for Arm PSCI functions | expand |
Thanks for the patch! please see my comment below, On Tue, Sep 16, 2025 at 3:14 AM G Edhaya Chandran <edhaya.chandran@arm.com> wrote: > From: edhay <edhaya.chandran@arm.com> > > Power State Coordination Interface (PSCI) is a standard to manage power > states in systems > based on Arm architectures—especially in multi-core or multi-cluster > environments. > PSCI provides a firmware interface that allows operating systems to control > the power states of CPUs and other system components in a standardized way. > Actual implementation of these calls reside in secure firmware like Arm > Trusted Firmware. > > Arm defines these interfaces in > Arm Power State Coordination Interface specification > https://developer.arm.com/documentation/den0022/latest/ > > Following new tests are added: > 1. Check if PSCI is supported as per ACPI FADT table > 2. Test for PSCI_VERSION > 3. Test for PSCI_FEATURES: Check for the implementation of all > the 22 PSCI functions > 4. Test for AFFINITY_INFO > 5. psci_features_bbr_check: Checks if all the mandatory PSCI functions > according to the Arm BBR specification is implemented. > enabled only with --sbbr or --ebbr flags > > This suite has dependency on smccc_test kernel module and the same > should loaded > > usage > sudo fwts psci > sudo fwts psci --sbbr > sudo fwts psci --ebbr > --- > smccc_test/smccc_test.c | 63 +++++ > smccc_test/smccc_test.h | 6 + > src/Makefile.am | 1 + > src/pci/smccc/psci.c | 560 ++++++++++++++++++++++++++++++++++++++++ > src/pci/smccc/smccc.c | 15 +- > 5 files changed, 640 insertions(+), 5 deletions(-) > create mode 100644 src/pci/smccc/psci.c > > diff --git a/smccc_test/smccc_test.c b/smccc_test/smccc_test.c > index b37691898852..c70add64fe6b 100644 > --- a/smccc_test/smccc_test.c > +++ b/smccc_test/smccc_test.c > @@ -85,6 +85,18 @@ MODULE_LICENSE("GPL"); > #define SMCCC_ARCH_SOC_ID 0x80000002 > #endif > > +#ifndef PSCI_VERSION > +#define PSCI_VERSION 0x84000000 > +#endif > + > +#ifndef PSCI_FEATURES > +#define PSCI_FEATURES 0x8400000A > +#endif > + > +#ifndef AFFINITY_INFO > +#define AFFINITY_INFO 0x84000004 > +#endif > + > /* > * smccc_test_copy_to_user() > * copy arm_res a* registers to user space test_arg w array > @@ -199,6 +211,51 @@ static long smccc_test_arch_soc_id(unsigned long arg) > return smccc_test_copy_to_user(arg, &arm_res, conduit); > } > > +static long smccc_test_psci_version(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(PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, > &arm_res); > + > + return smccc_test_copy_to_user(arg, &arm_res, conduit); > +} > + > +static long smccc_test_psci_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(PSCI_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_affinity_info(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(AFFINITY_INFO, test_arg.w[1], > test_arg.w[2], 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) > { > @@ -229,6 +286,12 @@ static long smccc_test_ioctl(struct file *file, > unsigned int cmd, > return smccc_test_arch_features(arg); > case SMCCC_TEST_ARCH_SOC_ID: > return smccc_test_arch_soc_id(arg); > + case SMCCC_TEST_PSCI_VERSION: > + return smccc_test_psci_version(arg); > + case SMCCC_TEST_PSCI_FEATURES: > + return smccc_test_psci_features(arg); > + case SMCCC_TEST_AFFINITY_INFO: > + return smccc_test_affinity_info(arg); > default: > break; > } > diff --git a/smccc_test/smccc_test.h b/smccc_test/smccc_test.h > index 6597d87e7b20..25c83a66e574 100644 > --- a/smccc_test/smccc_test.h > +++ b/smccc_test/smccc_test.h > @@ -45,4 +45,10 @@ struct smccc_test_arg { > _IOWR('p', 0x07, struct smccc_test_arg) > #define SMCCC_TEST_ARCH_SOC_ID \ > _IOWR('p', 0x08, struct smccc_test_arg) > +#define SMCCC_TEST_PSCI_VERSION \ > + _IOWR('p', 0x09, struct smccc_test_arg) > +#define SMCCC_TEST_PSCI_FEATURES \ > + _IOWR('p', 0x0A, struct smccc_test_arg) > +#define SMCCC_TEST_AFFINITY_INFO \ > + _IOWR('p', 0x0B, struct smccc_test_arg) > #endif > diff --git a/src/Makefile.am b/src/Makefile.am > index 2cdbe60f67e3..448318be3475 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -209,6 +209,7 @@ fwts_SOURCES = main.c \ > pci/crs/crs.c \ > pci/maxreadreq/maxreadreq.c \ > pci/smccc/smccc.c \ > + pci/smccc/psci.c \ > please sync up with latest source of fwts version, smccc has been move out from pci > tpm/tpmevlog/tpmevlog.c \ > tpm/tpmevlogdump/tpmevlogdump.c \ > uefi/csm/csm.c \ > diff --git a/src/pci/smccc/psci.c b/src/pci/smccc/psci.c > new file mode 100644 > index 000000000000..feb1b8179694 > --- /dev/null > +++ b/src/pci/smccc/psci.c > @@ -0,0 +1,560 @@ > +/* > + * > + * Copyright (C) 2025 Canonical > + * Copyright (C) 2025 Arm > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA > 02110-1301, USA. > + * > + */ > +#include "fwts.h" > + > +#ifdef FWTS_ARCH_AARCH64 > + > +#include <errno.h> > +#include <inttypes.h> > +#include <stdio.h> > +#include <stddef.h> > +#include <stdbool.h> > +#include <string.h> > +#include <sys/ioctl.h> > +#include <fcntl.h> > + > +#include "smccc_test.h" > + > +/* > + * ARM PSCI tests, > + * https://developer.arm.com/documentation/den0022/latest/ > + */ > + > +extern int smccc_init(fwts_framework *fw); > +extern char *smccc_pci_conduit_name(struct smccc_test_arg *arg); > +extern int smccc_deinit(fwts_framework *fw); > +extern int smccc_pci_conduit_check(fwts_framework *fw, struct > smccc_test_arg *arg); > +extern int smccc_fd; > + > +#define PSCI_VERSION 0x84000000 > +#define CPU_SUSPEND 0x84000001 > +#define CPU_OFF 0x84000002 > +#define CPU_ON 0x84000003 > +#define AFFINITY_INFO 0x84000004 > +#define MIGRATE 0x84000005 > +#define MIGRATE_INFO_TYPE 0x84000006 > +#define MIGRATE_INFO_UP_CPU 0x84000007 > +#define SYSTEM_OFF 0x84000008 > +#define SYSTEM_RESET 0x84000009 > +#define PSCI_FEATURES 0x8400000A > +#define CPU_FREEZE 0x8400000B > +#define CPU_DEFAULT_SUSPEND 0x8400000C > +#define NODE_HW_STATE 0x8400000D > +#define SYSTEM_SUSPEND 0x8400000E > +#define PSCI_SET_SUSPEND_MODE 0x8400000F > +#define PSCI_STAT_RESIDENCY 0x84000010 > +#define PSCI_STAT_COUNT 0x84000011 > +#define SYSTEM_RESET2 0x84000012 > +#define MEM_PROTECT 0x84000013 > +#define MEM_PROTECT_CHECK_RANGE 0x84000014 > +#define SYSTEM_OFF2 0x84000015 > minor, please align the above data > + > +#define PSCI_VERSION_0_2 0x00002 > +#define PSCI_VERSION_1_0 0x10000 > +#define PSCI_VERSION_1_1 0x10001 > +#define PSCI_VERSION_1_2 0x10002 > + > +enum PSCI_ret_code { > + PSCI_SUCCESS = 0, > + PSCI_NOT_SUPPORTED = -1 > +}; > + > +typedef struct { > + const uint32_t PSCI_func_id; > + const char *PSCI_func_id_name; > + bool implemented; > +} PSCI_func_id_t; > + > +static PSCI_func_id_t PSCI_func_id_list[] = { > + { PSCI_VERSION, "PSCI_VERSION", > false }, > + { CPU_SUSPEND, "CPU_SUSPEND", > false }, > + { CPU_OFF, "CPU_OFF", > false }, > + { CPU_ON, "CPU_ON", > false }, > + { AFFINITY_INFO, "AFFINITY_INFO", > false }, > + { MIGRATE, "MIGRATE", > false }, > + { MIGRATE_INFO_TYPE, "MIGRATE_INFO_TYPE", > false }, > + { MIGRATE_INFO_UP_CPU, "MIGRATE_INFO_UP_CPU ", false > }, > + { SYSTEM_OFF, "SYSTEM_OFF", > false }, > + { SYSTEM_RESET, "SYSTEM_RESET", > false }, > + { PSCI_FEATURES, "PSCI_FEATURES", > false }, > + { CPU_FREEZE, "CPU_FREEZE", > false }, > + { CPU_DEFAULT_SUSPEND, "CPU_DEFAULT_SUSPEND", > false }, > + { NODE_HW_STATE, "NODE_HW_STATE", > false }, > + { SYSTEM_SUSPEND, "SYSTEM_SUSPEND", > false }, > + { PSCI_SET_SUSPEND_MODE, "PSCI_SET_SUSPEND_MODE", false }, > + { PSCI_STAT_RESIDENCY, "PSCI_STAT_RESIDENCY", > false }, > + { PSCI_STAT_COUNT, "PSCI_STAT_COUNT", > false }, > + { SYSTEM_RESET2, "SYSTEM_RESET2", > false }, > + { MEM_PROTECT, "MEM_PROTECT", > false }, > + { MEM_PROTECT_CHECK_RANGE, "MEM_PROTECT_CHECK_RANGE", false }, > + { SYSTEM_OFF2, "SYSTEM_OFF2", > false } > +}; > minor, please align the above data > + > +static uint16_t psci_major_version = 0; > +static uint16_t psci_minor_version = 0; > +static uint32_t psci_version = 0; > + > +/* > + * check_for_psci_support() > + * This test checks if PSCI is supported in the system by checking > the FADT table > + */ > +static int check_for_psci_support(fwts_framework *fw) > +{ > + static const fwts_acpi_gas null_gas; > + fwts_acpi_table_info *table = NULL; > + const fwts_acpi_table_fadt *fadt; > + int fadt_size; > + bool passed = true; > + > + if (fwts_acpi_find_table(fw, "FACP", 0, &table) != FWTS_OK) { > + fwts_log_error(fw, "Cannot read ACPI table FACP."); > + return FWTS_ERROR; > + } > + if (table == NULL) { > + fwts_log_error(fw, "ACPI table FACP does not exist!"); > + return FWTS_ERROR; > + } > + > + fadt = (const fwts_acpi_table_fadt *)table->data; > + fadt_size = table->length; > + if (fadt_size == 0) { > + fwts_log_error(fw, "ACPI table FACP has zero length!"); > + return FWTS_ERROR; > + } > + > + bool PSCI_compliant = fadt->arm_boot_flags & > FWTS_FACP_ARM_BOOT_ARCH_PSCI_COMPLIANT; > + if ( fadt->arm_boot_flags & FWTS_FACP_ARM_BOOT_ARCH_PSCI_COMPLIANT) { > bool PSCI_compliant seems not necessary, just if (PSCI_compliant) { + fwts_log_info_verbatim(fw, "Arm PSCI: FADT ARM_BOOT_ARCH PSCI > compliant flag is set"); > + } > + else { > Minor, Please use the coding style for the else statement's braces, placing the opening brace on the same line as the keyword. i.e. } else { > + fwts_log_error(fw, "Arm PSCI: FADT ARM_BOOT_ARCH PSCI compliant > flag is not set." > + " PSCI is not supported"); > + return FWTS_ERROR; > + } > + > + bool HVC_Conduit = fadt->arm_boot_flags & > FWTS_FACP_ARM_BOOT_ARCH_PSCI_USE_HVC; > + if (HVC_Conduit) { > same here > + fwts_log_info_verbatim(fw, "Arm PSCI: PSCI uses HVC conduit"); > + } > + else { > same here > + fwts_log_info_verbatim(fw, "Arm PSCI: PSCI uses SMC conduit"); > + } > + > + if (memcmp((const void *)&fadt->sleep_control_reg, > + (const void *)&null_gas, > + sizeof(fwts_acpi_gas))) { > + passed = false; alignment? > + fwts_log_error(fw, "Arm PSCI: FADT SLEEP_CONTROL_REG is > not zeroed (Recommended)" > + ". Non-zero general register structure detected. Must be > coded as all zeros"); > + } + > + if (memcmp((const void *)&fadt->sleep_status_reg, > + (const void *)&null_gas, > + sizeof(fwts_acpi_gas))) { > + passed = false; > alignment? > + fwts_log_error(fw, "Arm PSCI: FADT SLEEP_STATUS_REG is not > zeroed (Recommended)" > + ". Non-zero general register structure detected. Must be > coded as all zeros"); > + } > + > + if (passed == false) > + { > + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SleepNonZero", > + "FADT SLEEP_* registers field are non-zero."); > + return (FWTS_ERROR); > + } > + > + fwts_passed(fw, "Check for Arm PSCI support test passed"); > + return FWTS_OK; > +} > + > + > +/* > + * psci_version_test() > + * test PSCI function VERSION > + */ > +static int psci_version_test(fwts_framework *fw) > +{ > + int ret; > + struct smccc_test_arg arg = { }; > + > + arg.size = sizeof(arg); > + arg.w[0] = PSCI_VERSION; > + > + ret = ioctl(smccc_fd, SMCCC_TEST_PSCI_VERSION, &arg); > + if (ret < 0) { > + fwts_log_error(fw, "SMCCC test driver ioctl > PSCI_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')", > PRIx32 instead of x? > + arg.conduit, smccc_pci_conduit_name(&arg)); > + > + psci_major_version = arg.w[0] >> 16; > + psci_minor_version = arg.w[0] & 0x0000ffff; > + fwts_log_info_verbatim(fw, "Arm PSCI: Major version: %d, Minor > version: %d", > + psci_major_version, psci_minor_version); > PRIu16 instead of d? > + > + psci_version = (psci_major_version << 16) + psci_minor_version; > + > + fwts_passed(fw, "Arm PSCI_VERSION test passed"); > + return FWTS_OK; > +} > + > + > +/* > + * get_implementation_status() > + * Query the implementation status by function_id > +*/ > +static bool get_implementation_status(const uint32_t func_id) > +{ > + int k=0; > + for (k=0; k<FWTS_ARRAY_SIZE(PSCI_func_id_list); k++) { > please add spacing around the less than operator (<) i.e. + for (k=0; k < FWTS_ARRAY_SIZE(PSCI_func_id_list); k++) { + if (PSCI_func_id_list[k].PSCI_func_id == func_id) > + return (PSCI_func_id_list[k].implemented); > + } > + > + return false; > +} > + > +/* > + * psci_features_bbr_check() > + * Asserts based on the BBR specification rules in > + * Section 4.2 which refers to the table 6.9 in the > + * PSCI specification > https://developer.arm.com/documentation/den0022/latest/ > + * This function can be run only if SBBR or EBBR flag is set > + * while running FWTS > + */ > + > extra empty line > + static int psci_features_bbr_check(fwts_framework *fw) > + { > + bool bbr_check_failed = false; > + uint16_t assert_failed_count = 0; > + int i=0; > + > + fwts_log_info_verbatim(fw, "Arm BBR PSCI implementation check - > the detected PSCI version is %d.%d", > + psci_major_version, psci_minor_version); > PRIu16? + > + for (i=0; i<FWTS_ARRAY_SIZE(PSCI_func_id_list); i++) { > add spacing around the less than operator (<) > + if(PSCI_func_id_list[i].implemented == false) { > + switch (PSCI_func_id_list[i].PSCI_func_id) { > + > + case PSCI_VERSION: > + case CPU_SUSPEND: > alignment? > + case CPU_OFF: > + case CPU_ON: > + case AFFINITY_INFO: > + case SYSTEM_OFF: > + case SYSTEM_RESET: { > + /* These PSCI functions are mandatory for all > versions */ > + bbr_check_failed = true; > + //fwts_log_info_verbatim(fw, "failed: The mandatory > function: %s is not implemented", > + // PSCI_func_id_list[i].PSCI_func_id_name); > Could you please remove the code above? It appears to be redundant. > + } > + break; > + > + case PSCI_FEATURES: { > + /* PSCI_FEATURES is mandatory for > all PSCI versions >= 1.0 */ > + if(psci_version >= PSCI_VERSION_1_0) please add spacing after "if" > + { > + bbr_check_failed = true; > + } > + } > + break; > + > + default: { > + fwts_log_info_verbatim(fw, "Info: > \"Optional\" Function: 0x%8.8" PRIx32 " (%s) is not Implemented.", > + > PSCI_func_id_list[i].PSCI_func_id, > + > PSCI_func_id_list[i].PSCI_func_id_name); > + } > + } > + if(bbr_check_failed == true) { > please add spacing after "if" > + fwts_log_error(fw, "failed: As per Arm BBR > specifciation, " > + "the implementation of %s is > \"Mandatory\" in PSCI v%d.%d but is not implemented" > + , > PSCI_func_id_list[i].PSCI_func_id_name > + , psci_major_version, > psci_minor_version); > + bbr_check_failed = false; > + > + assert_failed_count++; > + } > + } > + > + /* Additional checks */ > + /* Some PSCI functions are mandatory conditionally. Such cases > are checked below*/ > + if(PSCI_func_id_list[i].implemented == true) { > please add spacing after "if" > + switch (PSCI_func_id_list[i].PSCI_func_id) { > + case MIGRATE: { > + /* If MIGRATE is implemented then MIGRATE_INFO_UP_CPU > is mandatory*/ > + if (false == > get_implementation_status(MIGRATE_INFO_UP_CPU)) { > + fwts_log_error(fw, "failed: MIGRATE is > implemented however MIGRATE_INFO_UP_CPU" > + " is not implemented"); > + assert_failed_count++; > + } > + } > + break; > + > + case PSCI_STAT_RESIDENCY: { > + /* If PSCI_STAT_RESIDENCY is implemented then > PSCI_STAT_COUNT is mandatory*/ > spacing before */ > + if (false == > get_implementation_status(PSCI_STAT_COUNT)) { > + fwts_log_error(fw, "failed: PSCI_STAT_RESIDENCY > is implemented however PSCI_STAT_COUNT" > + " is not implemented"); > + assert_failed_count++; > + } > + } > + break; > + > + case PSCI_STAT_COUNT: { > + /* If PSCI_STAT_COUNT is implemented then > PSCI_STAT_RESIDENCY is mandatory*/ > spacing before */ > + if (false == > get_implementation_status(PSCI_STAT_RESIDENCY)) { > + fwts_log_error(fw, "failed: PSCI_STAT_COUNT is > implemented however PSCI_STAT_RESIDENCY" > + " is not implemented"); > + assert_failed_count++; > + } > + } > + break; > + > + case MEM_PROTECT: { > + /* If MEM_PROTECT is implemented then > MEM_PROTECT_CHECK_RANGE is mandatory*/ > same here > + if (false == > get_implementation_status(MEM_PROTECT_CHECK_RANGE)) { > + fwts_log_error(fw, "failed: MEM_PROTECT is > implemented however MEM_PROTECT_CHECK_RANGE" > + " is not implemented"); > + assert_failed_count++; > + } > + } > + break; > + > + case MEM_PROTECT_CHECK_RANGE: { > + /* If MEM_PROTECT_CHECK_RANGE is implemented then > MEM_PROTECT is mandatory*/ > same > + if (false == get_implementation_status(MEM_PROTECT)) { > + fwts_log_error(fw, "failed: > MEM_PROTECT_CHECK_RANGE is implemented however MEM_PROTECT" > + " is not implemented"); > + assert_failed_count++; > + } > + } > + break; > + } > + > + } > + } > please align "}" > + > + if (assert_failed_count > 0) { > + fwts_log_error(fw, "Arm SBBR check for Arm PSCI_FEATURES > failed. %d checks failed", > + assert_failed_count); > alignment? > + return FWTS_ERROR; > + } > + > + fwts_log_info_verbatim(fw, "Arm SBBR check for Arm PSCI_FEATURES > test passed"); > + return FWTS_OK; > + } > + > + > +/* > + * psci_features_test() > + * test PSCI function PSCI_FEATURES for each PSCI function > + */ > + > empty line? > + static int psci_features_test(fwts_framework *fw) > + { > + int ret; > + int i=0; > add spacing around "=" > + struct smccc_test_arg arg = { }; > + bool feaatures_test_failed = 0; > + > + if (psci_version == PSCI_VERSION_0_2) { > + fwts_skipped(fw, "PSCI_FEATURES is Not Applicable for PSCI > version %d.%d", > + psci_major_version, psci_minor_version); > PRIu? > + return FWTS_SKIP; > + } > + > + fwts_log_info_verbatim(fw, "========================= PSCI_FEATURES > query ==========================="); > + fwts_log_info_verbatim(fw, " # Function Name > Function ID Implementation Status"); > + for (i=0; i<FWTS_ARRAY_SIZE(PSCI_func_id_list); i++) { > + memset(&arg, 0, sizeof(arg)); > + arg.size = sizeof(arg); > + arg.w[0] = PSCI_FEATURES; > + > + //arg.w[1] should contain, the function id of the > + //PSCI function to query about, > please use /* */ instead of // > + arg.w[1] = PSCI_func_id_list[i].PSCI_func_id; > + > + ret = ioctl(smccc_fd, SMCCC_TEST_PSCI_FEATURES, &arg); > + if (ret < 0) { > + fwts_log_error(fw, "SMCCC test driver ioctl > PSCI_FEATURES " > + "failed, ret=%d, errno=%d (%s)\n", ret, > 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 == PSCI_NOT_SUPPORTED) { > + fwts_log_info_verbatim(fw, "%2d %-24s > 0x%8.8" PRIx32" NOT IMPLEMENTED", > + i+1, > + PSCI_func_id_list[i].PSCI_func_id_name, > + PSCI_func_id_list[i].PSCI_func_id); > allignment? > + PSCI_func_id_list[i].implemented = false; > + } > + else if (((arg.w[0] >> 31) & 0x1) == 0) { /* if bit 31 is > 0, then the function is supported */ > } else if (((arg.w[0] >> 31) & 0x1) == 0) { /* if bit 31 is 0, then the function is supported */ > + fwts_log_info_verbatim(fw, "%2d %-24s > 0x%8.8" PRIx32" IMPLEMENTED", > + i+1, > + PSCI_func_id_list[i].PSCI_func_id_name, > + PSCI_func_id_list[i].PSCI_func_id); > allignment? > + PSCI_func_id_list[i].implemented = true; > + } > + else if (((arg.w[0] >> 31) & 0x1) == 1) { /* if bit 31 is > 1, then the implementation is incorrect as per spec*/ > } else if spaceing before */ > + fwts_log_info_verbatim(fw, "%2d %-24s 0x%8.8" > PRIx32" " > + " Error Invalid return - Please check the > implementation", > + i+1, > + PSCI_func_id_list[i].PSCI_func_id_name, > + PSCI_func_id_list[i].PSCI_func_id); > + PSCI_func_id_list[i].implemented = false; > + feaatures_test_failed = 1; > + } > + } > + fwts_log_info_verbatim(fw, "\n"); > use fwts_log_nl(fw); for a new line > + //if --sbbr flag or --ebbr is set, additionally call the BBR > assertions function > /* */ > + if (fw->flags & FWTS_FLAG_SBBR || fw->flags & FWTS_FLAG_EBBR) { > + if (psci_features_bbr_check(fw) == FWTS_ERROR) { > + fwts_failed(fw, LOG_LEVEL_HIGH, > "psci_features_bbr_check check ", > + "for Arm BBR specification rules > failed.\n"); > + return (FWTS_ERROR); > + } > alignment? > + } > + > + if(feaatures_test_failed == 1) { > + fwts_failed(fw, LOG_LEVEL_HIGH, "Arm Arm PSCI_FEATURES test", > "failed"); > + return FWTS_ERROR; > + } > + > + fwts_passed(fw, "Arm PSCI_FEATURES test passed"); > + return FWTS_OK; > + } > + > + > + > extra empty line? > +static inline uint64_t read_mpidr_el1(void) > +{ > + uint64_t mpidr; > + asm volatile("mrs %0, mpidr_el1" : "=r" (mpidr)); > + return mpidr; > +} > + > +static int affinity_info_test(fwts_framework *fw) > +{ > + int ret; > + struct smccc_test_arg arg = { }; > + > alignment? > + uint64_t mpidr = read_mpidr_el1(); > + fwts_log_info_verbatim(fw, "Info: mpidr = 0x%16.16" PRIx64"", mpidr); > + > + > + uint32_t aff0 = mpidr & 0xFF; > + uint32_t aff1 = (mpidr >> 8) & 0xFF; > + uint32_t aff2 = (mpidr >> 16) & 0xFF; > + /* aff3 is unused */ > + > + uint32_t target_affinity = 0; > + target_affinity |= aff0; > + target_affinity |= (aff1 << 8); > + target_affinity |= (aff2 << 16); > + > + fwts_log_info_verbatim(fw, "Info: target_affinity = 0x%8.8" > PRIx32"", target_affinity); > + > alignment? > + uint32_t lowest_affinity_level = 0; /* All affinity levels are valid > */ > + > + arg.size = sizeof(arg); > + arg.w[0] = AFFINITY_INFO; > + arg.w[1] = target_affinity; > + arg.w[2] = lowest_affinity_level; > + > + ret = ioctl(smccc_fd, SMCCC_TEST_AFFINITY_INFO, &arg); > + if (ret < 0) { > + fwts_log_error(fw, "SMCCC test driver ioctl > SMCCC_TEST_AFFINITY_INFO " > + "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 result = arg.w[0]; > + switch(result) { > + case 2: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d (ON_PENDING)", > result); > + } > + break; > + case 1: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d (OFF)", result); > + } > + break; > + case 0: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d (ON)", result); > + } > + break; > + case -2: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d > (INVALID_PARAMETERS)", result); > + } > + break; > + case -8: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d (DISABLED)", > result); > + } > + break; > + default: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d , an unexpected > value", result); > + fwts_failed(fw, LOG_LEVEL_HIGH, "Arm PSCI AFFINITY_INFO ", > + "test failed"); > + return FWTS_ERROR; > + } > + } > + > + fwts_passed(fw, "Arm PSCI AFFINITY_INFO test passed"); > + return FWTS_OK; > +} > + > +static fwts_framework_minor_test psci_tests[] = { > + { check_for_psci_support, "Check for Arm PSCI support" }, > + { psci_version_test, "Test PSCI_VERSION" }, > + { psci_features_test, "Test PSCI_FEATURES" }, > + { affinity_info_test, "Test AFFINITY_INFO" }, > + { NULL, NULL } > +}; > alignment? > + > +static fwts_framework_ops psciops = { > + .description = "ARM64 PSCI tests.", > + .init = smccc_init, > + .deinit = smccc_deinit, > + .minor_tests = psci_tests > +}; > + > +FWTS_REGISTER("psci", &psciops, FWTS_TEST_ANYTIME, FWTS_FLAG_UNSAFE | > FWTS_FLAG_ROOT_PRIV) > + > + #endif > diff --git a/src/pci/smccc/smccc.c b/src/pci/smccc/smccc.c > index 144a0a24839b..fc477f2417b8 100644 > --- a/src/pci/smccc/smccc.c > +++ b/src/pci/smccc/smccc.c > @@ -84,7 +84,7 @@ static pci_func_id_t pci_func_ids[] = { > static const char *module_name = "smccc_test"; > static const char *dev_name = "/dev/smccc_test"; > static bool module_loaded; > -static int smccc_fd = -1; > +int smccc_fd = -1; > > typedef struct { > const uint32_t SMCCC_func_id; > @@ -103,7 +103,12 @@ static SMCCC_func_id_t SMCCC_func_id_list[] = { > static uint16_t smccc_major_version = 0; > static uint16_t smccc_minor_version = 0; > > -static int smccc_init(fwts_framework *fw) > +int smccc_init(fwts_framework *fw); > +int smccc_deinit(fwts_framework *fw); > +char *smccc_pci_conduit_name(struct smccc_test_arg *arg); > +int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg > *arg); > + > +int smccc_init(fwts_framework *fw) > { > if (fwts_module_load(fw, module_name) != FWTS_OK) { > module_loaded = false; > @@ -121,7 +126,7 @@ static int smccc_init(fwts_framework *fw) > return FWTS_OK; > } > > -static int smccc_deinit(fwts_framework *fw) > +int smccc_deinit(fwts_framework *fw) > { > if (smccc_fd >= 0) { > close(smccc_fd); > @@ -140,7 +145,7 @@ static int smccc_deinit(fwts_framework *fw) > * smccc_pci_conduit_name() > * map the conduit number to human readable string > */ > -static char *smccc_pci_conduit_name(struct smccc_test_arg *arg) > +char *smccc_pci_conduit_name(struct smccc_test_arg *arg) > { > static char unknown[32]; > > @@ -163,7 +168,7 @@ static char *smccc_pci_conduit_name(struct > smccc_test_arg *arg) > * smccc_pci_conduit_check() > * check if conduit number is valid > */ > -static int smccc_pci_conduit_check(fwts_framework *fw, struct > smccc_test_arg *arg) > +int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg > *arg) > { > switch (arg->conduit) { > case FWTS_SMCCC_CONDUIT_HVC: > Please move the shared part of the function to the src/lib/src directory to make it a library function instead of changing its scope limited, especially for the init and deinit functions. Cheers, Ivan > -- > 2.34.1 > >
Hi Ivan, Thank you for the review comments. Apologies for the alignment issues, there was an issue with my editor. Attached to the mail is reworked patch. With Warm Regards, Edhay From: Ivan Hu <ivan.hu@canonical.com> Sent: 18 September 2025 01:34 To: G Edhaya Chandran <Edhaya.Chandran@arm.com> Cc: fwts-devel@lists.ubuntu.com Subject: Re: [PATCH 1/1] pci/smccc: New tests for Arm PSCI functions Thanks for the patch! please see my comment below, On Tue, Sep 16, 2025 at 3:14 AM G Edhaya Chandran <edhaya.chandran@arm.com<mailto:edhaya.chandran@arm.com>> wrote: From: edhay <edhaya.chandran@arm.com<mailto:edhaya.chandran@arm.com>> Power State Coordination Interface (PSCI) is a standard to manage power states in systems based on Arm architectures—especially in multi-core or multi-cluster environments. PSCI provides a firmware interface that allows operating systems to control the power states of CPUs and other system components in a standardized way. Actual implementation of these calls reside in secure firmware like Arm Trusted Firmware. Arm defines these interfaces in Arm Power State Coordination Interface specification https://developer.arm.com/documentation/den0022/latest/ Following new tests are added: 1. Check if PSCI is supported as per ACPI FADT table 2. Test for PSCI_VERSION 3. Test for PSCI_FEATURES: Check for the implementation of all the 22 PSCI functions 4. Test for AFFINITY_INFO 5. psci_features_bbr_check: Checks if all the mandatory PSCI functions according to the Arm BBR specification is implemented. enabled only with --sbbr or --ebbr flags This suite has dependency on smccc_test kernel module and the same should loaded usage sudo fwts psci sudo fwts psci --sbbr sudo fwts psci --ebbr --- smccc_test/smccc_test.c | 63 +++++ smccc_test/smccc_test.h | 6 + src/Makefile.am | 1 + src/pci/smccc/psci.c | 560 ++++++++++++++++++++++++++++++++++++++++ src/pci/smccc/smccc.c | 15 +- 5 files changed, 640 insertions(+), 5 deletions(-) create mode 100644 src/pci/smccc/psci.c diff --git a/smccc_test/smccc_test.c b/smccc_test/smccc_test.c index b37691898852..c70add64fe6b 100644 --- a/smccc_test/smccc_test.c +++ b/smccc_test/smccc_test.c @@ -85,6 +85,18 @@ MODULE_LICENSE("GPL"); #define SMCCC_ARCH_SOC_ID 0x80000002 #endif +#ifndef PSCI_VERSION +#define PSCI_VERSION 0x84000000 +#endif + +#ifndef PSCI_FEATURES +#define PSCI_FEATURES 0x8400000A +#endif + +#ifndef AFFINITY_INFO +#define AFFINITY_INFO 0x84000004 +#endif + /* * smccc_test_copy_to_user() * copy arm_res a* registers to user space test_arg w array @@ -199,6 +211,51 @@ static long smccc_test_arch_soc_id(unsigned long arg) return smccc_test_copy_to_user(arg, &arm_res, conduit); } +static long smccc_test_psci_version(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(PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &arm_res); + + return smccc_test_copy_to_user(arg, &arm_res, conduit); +} + +static long smccc_test_psci_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(PSCI_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_affinity_info(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(AFFINITY_INFO, test_arg.w[1], test_arg.w[2], 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) { @@ -229,6 +286,12 @@ static long smccc_test_ioctl(struct file *file, unsigned int cmd, return smccc_test_arch_features(arg); case SMCCC_TEST_ARCH_SOC_ID: return smccc_test_arch_soc_id(arg); + case SMCCC_TEST_PSCI_VERSION: + return smccc_test_psci_version(arg); + case SMCCC_TEST_PSCI_FEATURES: + return smccc_test_psci_features(arg); + case SMCCC_TEST_AFFINITY_INFO: + return smccc_test_affinity_info(arg); default: break; } diff --git a/smccc_test/smccc_test.h b/smccc_test/smccc_test.h index 6597d87e7b20..25c83a66e574 100644 --- a/smccc_test/smccc_test.h +++ b/smccc_test/smccc_test.h @@ -45,4 +45,10 @@ struct smccc_test_arg { _IOWR('p', 0x07, struct smccc_test_arg) #define SMCCC_TEST_ARCH_SOC_ID \ _IOWR('p', 0x08, struct smccc_test_arg) +#define SMCCC_TEST_PSCI_VERSION \ + _IOWR('p', 0x09, struct smccc_test_arg) +#define SMCCC_TEST_PSCI_FEATURES \ + _IOWR('p', 0x0A, struct smccc_test_arg) +#define SMCCC_TEST_AFFINITY_INFO \ + _IOWR('p', 0x0B, struct smccc_test_arg) #endif diff --git a/src/Makefile.am b/src/Makefile.am index 2cdbe60f67e3..448318be3475 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -209,6 +209,7 @@ fwts_SOURCES = main.c \ pci/crs/crs.c \ pci/maxreadreq/maxreadreq.c \ pci/smccc/smccc.c \ + pci/smccc/psci.c \ please sync up with latest source of fwts version, smccc has been move out from pci tpm/tpmevlog/tpmevlog.c \ tpm/tpmevlogdump/tpmevlogdump.c \ uefi/csm/csm.c \ diff --git a/src/pci/smccc/psci.c b/src/pci/smccc/psci.c new file mode 100644 index 000000000000..feb1b8179694 --- /dev/null +++ b/src/pci/smccc/psci.c @@ -0,0 +1,560 @@ +/* + * + * Copyright (C) 2025 Canonical + * Copyright (C) 2025 Arm + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "fwts.h" + +#ifdef FWTS_ARCH_AARCH64 + +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stddef.h> +#include <stdbool.h> +#include <string.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#include "smccc_test.h" + +/* + * ARM PSCI tests, + * https://developer.arm.com/documentation/den0022/latest/ + */ + +extern int smccc_init(fwts_framework *fw); +extern char *smccc_pci_conduit_name(struct smccc_test_arg *arg); +extern int smccc_deinit(fwts_framework *fw); +extern int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg *arg); +extern int smccc_fd; + +#define PSCI_VERSION 0x84000000 +#define CPU_SUSPEND 0x84000001 +#define CPU_OFF 0x84000002 +#define CPU_ON 0x84000003 +#define AFFINITY_INFO 0x84000004 +#define MIGRATE 0x84000005 +#define MIGRATE_INFO_TYPE 0x84000006 +#define MIGRATE_INFO_UP_CPU 0x84000007 +#define SYSTEM_OFF 0x84000008 +#define SYSTEM_RESET 0x84000009 +#define PSCI_FEATURES 0x8400000A +#define CPU_FREEZE 0x8400000B +#define CPU_DEFAULT_SUSPEND 0x8400000C +#define NODE_HW_STATE 0x8400000D +#define SYSTEM_SUSPEND 0x8400000E +#define PSCI_SET_SUSPEND_MODE 0x8400000F +#define PSCI_STAT_RESIDENCY 0x84000010 +#define PSCI_STAT_COUNT 0x84000011 +#define SYSTEM_RESET2 0x84000012 +#define MEM_PROTECT 0x84000013 +#define MEM_PROTECT_CHECK_RANGE 0x84000014 +#define SYSTEM_OFF2 0x84000015 minor, please align the above data + +#define PSCI_VERSION_0_2 0x00002 +#define PSCI_VERSION_1_0 0x10000 +#define PSCI_VERSION_1_1 0x10001 +#define PSCI_VERSION_1_2 0x10002 + +enum PSCI_ret_code { + PSCI_SUCCESS = 0, + PSCI_NOT_SUPPORTED = -1 +}; + +typedef struct { + const uint32_t PSCI_func_id; + const char *PSCI_func_id_name; + bool implemented; +} PSCI_func_id_t; + +static PSCI_func_id_t PSCI_func_id_list[] = { + { PSCI_VERSION, "PSCI_VERSION", false }, + { CPU_SUSPEND, "CPU_SUSPEND", false }, + { CPU_OFF, "CPU_OFF", false }, + { CPU_ON, "CPU_ON", false }, + { AFFINITY_INFO, "AFFINITY_INFO", false }, + { MIGRATE, "MIGRATE", false }, + { MIGRATE_INFO_TYPE, "MIGRATE_INFO_TYPE", false }, + { MIGRATE_INFO_UP_CPU, "MIGRATE_INFO_UP_CPU ", false }, + { SYSTEM_OFF, "SYSTEM_OFF", false }, + { SYSTEM_RESET, "SYSTEM_RESET", false }, + { PSCI_FEATURES, "PSCI_FEATURES", false }, + { CPU_FREEZE, "CPU_FREEZE", false }, + { CPU_DEFAULT_SUSPEND, "CPU_DEFAULT_SUSPEND", false }, + { NODE_HW_STATE, "NODE_HW_STATE", false }, + { SYSTEM_SUSPEND, "SYSTEM_SUSPEND", false }, + { PSCI_SET_SUSPEND_MODE, "PSCI_SET_SUSPEND_MODE", false }, + { PSCI_STAT_RESIDENCY, "PSCI_STAT_RESIDENCY", false }, + { PSCI_STAT_COUNT, "PSCI_STAT_COUNT", false }, + { SYSTEM_RESET2, "SYSTEM_RESET2", false }, + { MEM_PROTECT, "MEM_PROTECT", false }, + { MEM_PROTECT_CHECK_RANGE, "MEM_PROTECT_CHECK_RANGE", false }, + { SYSTEM_OFF2, "SYSTEM_OFF2", false } +}; minor, please align the above data + +static uint16_t psci_major_version = 0; +static uint16_t psci_minor_version = 0; +static uint32_t psci_version = 0; + +/* + * check_for_psci_support() + * This test checks if PSCI is supported in the system by checking the FADT table + */ +static int check_for_psci_support(fwts_framework *fw) +{ + static const fwts_acpi_gas null_gas; + fwts_acpi_table_info *table = NULL; + const fwts_acpi_table_fadt *fadt; + int fadt_size; + bool passed = true; + + if (fwts_acpi_find_table(fw, "FACP", 0, &table) != FWTS_OK) { + fwts_log_error(fw, "Cannot read ACPI table FACP."); + return FWTS_ERROR; + } + if (table == NULL) { + fwts_log_error(fw, "ACPI table FACP does not exist!"); + return FWTS_ERROR; + } + + fadt = (const fwts_acpi_table_fadt *)table->data; + fadt_size = table->length; + if (fadt_size == 0) { + fwts_log_error(fw, "ACPI table FACP has zero length!"); + return FWTS_ERROR; + } + + bool PSCI_compliant = fadt->arm_boot_flags & FWTS_FACP_ARM_BOOT_ARCH_PSCI_COMPLIANT; + if ( fadt->arm_boot_flags & FWTS_FACP_ARM_BOOT_ARCH_PSCI_COMPLIANT) { bool PSCI_compliant seems not necessary, just if (PSCI_compliant) { + fwts_log_info_verbatim(fw, "Arm PSCI: FADT ARM_BOOT_ARCH PSCI compliant flag is set"); + } + else { Minor, Please use the coding style for the else statement's braces, placing the opening brace on the same line as the keyword. i.e. } else { + fwts_log_error(fw, "Arm PSCI: FADT ARM_BOOT_ARCH PSCI compliant flag is not set." + " PSCI is not supported"); + return FWTS_ERROR; + } + + bool HVC_Conduit = fadt->arm_boot_flags & FWTS_FACP_ARM_BOOT_ARCH_PSCI_USE_HVC; + if (HVC_Conduit) { same here + fwts_log_info_verbatim(fw, "Arm PSCI: PSCI uses HVC conduit"); + } + else { same here + fwts_log_info_verbatim(fw, "Arm PSCI: PSCI uses SMC conduit"); + } + + if (memcmp((const void *)&fadt->sleep_control_reg, + (const void *)&null_gas, + sizeof(fwts_acpi_gas))) { + passed = false; alignment? + fwts_log_error(fw, "Arm PSCI: FADT SLEEP_CONTROL_REG is not zeroed (Recommended)" + ". Non-zero general register structure detected. Must be coded as all zeros"); + } + + if (memcmp((const void *)&fadt->sleep_status_reg, + (const void *)&null_gas, + sizeof(fwts_acpi_gas))) { + passed = false; alignment? + fwts_log_error(fw, "Arm PSCI: FADT SLEEP_STATUS_REG is not zeroed (Recommended)" + ". Non-zero general register structure detected. Must be coded as all zeros"); + } + + if (passed == false) + { + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SleepNonZero", + "FADT SLEEP_* registers field are non-zero."); + return (FWTS_ERROR); + } + + fwts_passed(fw, "Check for Arm PSCI support test passed"); + return FWTS_OK; +} + + +/* + * psci_version_test() + * test PSCI function VERSION + */ +static int psci_version_test(fwts_framework *fw) +{ + int ret; + struct smccc_test_arg arg = { }; + + arg.size = sizeof(arg); + arg.w[0] = PSCI_VERSION; + + ret = ioctl(smccc_fd, SMCCC_TEST_PSCI_VERSION, &arg); + if (ret < 0) { + fwts_log_error(fw, "SMCCC test driver ioctl PSCI_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')", PRIx32 instead of x? + arg.conduit, smccc_pci_conduit_name(&arg)); + + psci_major_version = arg.w[0] >> 16; + psci_minor_version = arg.w[0] & 0x0000ffff; + fwts_log_info_verbatim(fw, "Arm PSCI: Major version: %d, Minor version: %d", + psci_major_version, psci_minor_version); PRIu16 instead of d? + + psci_version = (psci_major_version << 16) + psci_minor_version; + + fwts_passed(fw, "Arm PSCI_VERSION test passed"); + return FWTS_OK; +} + + +/* + * get_implementation_status() + * Query the implementation status by function_id +*/ +static bool get_implementation_status(const uint32_t func_id) +{ + int k=0; + for (k=0; k<FWTS_ARRAY_SIZE(PSCI_func_id_list); k++) { please add spacing around the less than operator (<) i.e. + for (k=0; k < FWTS_ARRAY_SIZE(PSCI_func_id_list); k++) { + if (PSCI_func_id_list[k].PSCI_func_id == func_id) + return (PSCI_func_id_list[k].implemented); + } + + return false; +} + +/* + * psci_features_bbr_check() + * Asserts based on the BBR specification rules in + * Section 4.2 which refers to the table 6.9 in the + * PSCI specification https://developer.arm.com/documentation/den0022/latest/ + * This function can be run only if SBBR or EBBR flag is set + * while running FWTS + */ + extra empty line + static int psci_features_bbr_check(fwts_framework *fw) + { + bool bbr_check_failed = false; + uint16_t assert_failed_count = 0; + int i=0; + + fwts_log_info_verbatim(fw, "Arm BBR PSCI implementation check - the detected PSCI version is %d.%d", + psci_major_version, psci_minor_version); PRIu16? + + for (i=0; i<FWTS_ARRAY_SIZE(PSCI_func_id_list); i++) { add spacing around the less than operator (<) + if(PSCI_func_id_list[i].implemented == false) { + switch (PSCI_func_id_list[i].PSCI_func_id) { + + case PSCI_VERSION: + case CPU_SUSPEND: alignment? + case CPU_OFF: + case CPU_ON: + case AFFINITY_INFO: + case SYSTEM_OFF: + case SYSTEM_RESET: { + /* These PSCI functions are mandatory for all versions */ + bbr_check_failed = true; + //fwts_log_info_verbatim(fw, "failed: The mandatory function: %s is not implemented", + // PSCI_func_id_list[i].PSCI_func_id_name); Could you please remove the code above? It appears to be redundant. + } + break; + + case PSCI_FEATURES: { + /* PSCI_FEATURES is mandatory for all PSCI versions >= 1.0 */ + if(psci_version >= PSCI_VERSION_1_0) please add spacing after "if" + { + bbr_check_failed = true; + } + } + break; + + default: { + fwts_log_info_verbatim(fw, "Info: \"Optional\" Function: 0x%8.8" PRIx32 " (%s) is not Implemented.", + PSCI_func_id_list[i].PSCI_func_id, + PSCI_func_id_list[i].PSCI_func_id_name); + } + } + if(bbr_check_failed == true) { please add spacing after "if" + fwts_log_error(fw, "failed: As per Arm BBR specifciation, " + "the implementation of %s is \"Mandatory\" in PSCI v%d.%d but is not implemented" + , PSCI_func_id_list[i].PSCI_func_id_name + , psci_major_version, psci_minor_version); + bbr_check_failed = false; + + assert_failed_count++; + } + } + + /* Additional checks */ + /* Some PSCI functions are mandatory conditionally. Such cases are checked below*/ + if(PSCI_func_id_list[i].implemented == true) { please add spacing after "if" + switch (PSCI_func_id_list[i].PSCI_func_id) { + case MIGRATE: { + /* If MIGRATE is implemented then MIGRATE_INFO_UP_CPU is mandatory*/ + if (false == get_implementation_status(MIGRATE_INFO_UP_CPU)) { + fwts_log_error(fw, "failed: MIGRATE is implemented however MIGRATE_INFO_UP_CPU" + " is not implemented"); + assert_failed_count++; + } + } + break; + + case PSCI_STAT_RESIDENCY: { + /* If PSCI_STAT_RESIDENCY is implemented then PSCI_STAT_COUNT is mandatory*/ spacing before */ + if (false == get_implementation_status(PSCI_STAT_COUNT)) { + fwts_log_error(fw, "failed: PSCI_STAT_RESIDENCY is implemented however PSCI_STAT_COUNT" + " is not implemented"); + assert_failed_count++; + } + } + break; + + case PSCI_STAT_COUNT: { + /* If PSCI_STAT_COUNT is implemented then PSCI_STAT_RESIDENCY is mandatory*/ spacing before */ + if (false == get_implementation_status(PSCI_STAT_RESIDENCY)) { + fwts_log_error(fw, "failed: PSCI_STAT_COUNT is implemented however PSCI_STAT_RESIDENCY" + " is not implemented"); + assert_failed_count++; + } + } + break; + + case MEM_PROTECT: { + /* If MEM_PROTECT is implemented then MEM_PROTECT_CHECK_RANGE is mandatory*/ same here + if (false == get_implementation_status(MEM_PROTECT_CHECK_RANGE)) { + fwts_log_error(fw, "failed: MEM_PROTECT is implemented however MEM_PROTECT_CHECK_RANGE" + " is not implemented"); + assert_failed_count++; + } + } + break; + + case MEM_PROTECT_CHECK_RANGE: { + /* If MEM_PROTECT_CHECK_RANGE is implemented then MEM_PROTECT is mandatory*/ same + if (false == get_implementation_status(MEM_PROTECT)) { + fwts_log_error(fw, "failed: MEM_PROTECT_CHECK_RANGE is implemented however MEM_PROTECT" + " is not implemented"); + assert_failed_count++; + } + } + break; + } + + } + } please align "}" + + if (assert_failed_count > 0) { + fwts_log_error(fw, "Arm SBBR check for Arm PSCI_FEATURES failed. %d checks failed", + assert_failed_count); alignment? + return FWTS_ERROR; + } + + fwts_log_info_verbatim(fw, "Arm SBBR check for Arm PSCI_FEATURES test passed"); + return FWTS_OK; + } + + +/* + * psci_features_test() + * test PSCI function PSCI_FEATURES for each PSCI function + */ + empty line? + static int psci_features_test(fwts_framework *fw) + { + int ret; + int i=0; add spacing around "=" + struct smccc_test_arg arg = { }; + bool feaatures_test_failed = 0; + + if (psci_version == PSCI_VERSION_0_2) { + fwts_skipped(fw, "PSCI_FEATURES is Not Applicable for PSCI version %d.%d", + psci_major_version, psci_minor_version); PRIu? + return FWTS_SKIP; + } + + fwts_log_info_verbatim(fw, "========================= PSCI_FEATURES query ==========================="); + fwts_log_info_verbatim(fw, " # Function Name Function ID Implementation Status"); + for (i=0; i<FWTS_ARRAY_SIZE(PSCI_func_id_list); i++) { + memset(&arg, 0, sizeof(arg)); + arg.size = sizeof(arg); + arg.w[0] = PSCI_FEATURES; + + //arg.w[1] should contain, the function id of the + //PSCI function to query about, please use /* */ instead of // + arg.w[1] = PSCI_func_id_list[i].PSCI_func_id; + + ret = ioctl(smccc_fd, SMCCC_TEST_PSCI_FEATURES, &arg); + if (ret < 0) { + fwts_log_error(fw, "SMCCC test driver ioctl PSCI_FEATURES " + "failed, ret=%d, errno=%d (%s)\n", ret, 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 == PSCI_NOT_SUPPORTED) { + fwts_log_info_verbatim(fw, "%2d %-24s 0x%8.8" PRIx32" NOT IMPLEMENTED", + i+1, + PSCI_func_id_list[i].PSCI_func_id_name, + PSCI_func_id_list[i].PSCI_func_id); allignment? + PSCI_func_id_list[i].implemented = false; + } + else if (((arg.w[0] >> 31) & 0x1) == 0) { /* if bit 31 is 0, then the function is supported */ } else if (((arg.w[0] >> 31) & 0x1) == 0) { /* if bit 31 is 0, then the function is supported */ + fwts_log_info_verbatim(fw, "%2d %-24s 0x%8.8" PRIx32" IMPLEMENTED", + i+1, + PSCI_func_id_list[i].PSCI_func_id_name, + PSCI_func_id_list[i].PSCI_func_id); allignment? + PSCI_func_id_list[i].implemented = true; + } + else if (((arg.w[0] >> 31) & 0x1) == 1) { /* if bit 31 is 1, then the implementation is incorrect as per spec*/ } else if spaceing before */ + fwts_log_info_verbatim(fw, "%2d %-24s 0x%8.8" PRIx32" " + " Error Invalid return - Please check the implementation", + i+1, + PSCI_func_id_list[i].PSCI_func_id_name, + PSCI_func_id_list[i].PSCI_func_id); + PSCI_func_id_list[i].implemented = false; + feaatures_test_failed = 1; + } + } + fwts_log_info_verbatim(fw, "\n"); use fwts_log_nl(fw); for a new line + //if --sbbr flag or --ebbr is set, additionally call the BBR assertions function /* */ + if (fw->flags & FWTS_FLAG_SBBR || fw->flags & FWTS_FLAG_EBBR) { + if (psci_features_bbr_check(fw) == FWTS_ERROR) { + fwts_failed(fw, LOG_LEVEL_HIGH, "psci_features_bbr_check check ", + "for Arm BBR specification rules failed.\n"); + return (FWTS_ERROR); + } alignment? + } + + if(feaatures_test_failed == 1) { + fwts_failed(fw, LOG_LEVEL_HIGH, "Arm Arm PSCI_FEATURES test", "failed"); + return FWTS_ERROR; + } + + fwts_passed(fw, "Arm PSCI_FEATURES test passed"); + return FWTS_OK; + } + + + extra empty line? +static inline uint64_t read_mpidr_el1(void) +{ + uint64_t mpidr; + asm volatile("mrs %0, mpidr_el1" : "=r" (mpidr)); + return mpidr; +} + +static int affinity_info_test(fwts_framework *fw) +{ + int ret; + struct smccc_test_arg arg = { }; + alignment? + uint64_t mpidr = read_mpidr_el1(); + fwts_log_info_verbatim(fw, "Info: mpidr = 0x%16.16" PRIx64"", mpidr); + + + uint32_t aff0 = mpidr & 0xFF; + uint32_t aff1 = (mpidr >> 8) & 0xFF; + uint32_t aff2 = (mpidr >> 16) & 0xFF; + /* aff3 is unused */ + + uint32_t target_affinity = 0; + target_affinity |= aff0; + target_affinity |= (aff1 << 8); + target_affinity |= (aff2 << 16); + + fwts_log_info_verbatim(fw, "Info: target_affinity = 0x%8.8" PRIx32"", target_affinity); + alignment? + uint32_t lowest_affinity_level = 0; /* All affinity levels are valid */ + + arg.size = sizeof(arg); + arg.w[0] = AFFINITY_INFO; + arg.w[1] = target_affinity; + arg.w[2] = lowest_affinity_level; + + ret = ioctl(smccc_fd, SMCCC_TEST_AFFINITY_INFO, &arg); + if (ret < 0) { + fwts_log_error(fw, "SMCCC test driver ioctl SMCCC_TEST_AFFINITY_INFO " + "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 result = arg.w[0]; + switch(result) { + case 2: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d (ON_PENDING)", result); + } + break; + case 1: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d (OFF)", result); + } + break; + case 0: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d (ON)", result); + } + break; + case -2: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d (INVALID_PARAMETERS)", result); + } + break; + case -8: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d (DISABLED)", result); + } + break; + default: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d , an unexpected value", result); + fwts_failed(fw, LOG_LEVEL_HIGH, "Arm PSCI AFFINITY_INFO ", + "test failed"); + return FWTS_ERROR; + } + } + + fwts_passed(fw, "Arm PSCI AFFINITY_INFO test passed"); + return FWTS_OK; +} + +static fwts_framework_minor_test psci_tests[] = { + { check_for_psci_support, "Check for Arm PSCI support" }, + { psci_version_test, "Test PSCI_VERSION" }, + { psci_features_test, "Test PSCI_FEATURES" }, + { affinity_info_test, "Test AFFINITY_INFO" }, + { NULL, NULL } +}; alignment? + +static fwts_framework_ops psciops = { + .description = "ARM64 PSCI tests.", + .init = smccc_init, + .deinit = smccc_deinit, + .minor_tests = psci_tests +}; + +FWTS_REGISTER("psci", &psciops, FWTS_TEST_ANYTIME, FWTS_FLAG_UNSAFE | FWTS_FLAG_ROOT_PRIV) + + #endif diff --git a/src/pci/smccc/smccc.c b/src/pci/smccc/smccc.c index 144a0a24839b..fc477f2417b8 100644 --- a/src/pci/smccc/smccc.c +++ b/src/pci/smccc/smccc.c @@ -84,7 +84,7 @@ static pci_func_id_t pci_func_ids[] = { static const char *module_name = "smccc_test"; static const char *dev_name = "/dev/smccc_test"; static bool module_loaded; -static int smccc_fd = -1; +int smccc_fd = -1; typedef struct { const uint32_t SMCCC_func_id; @@ -103,7 +103,12 @@ static SMCCC_func_id_t SMCCC_func_id_list[] = { static uint16_t smccc_major_version = 0; static uint16_t smccc_minor_version = 0; -static int smccc_init(fwts_framework *fw) +int smccc_init(fwts_framework *fw); +int smccc_deinit(fwts_framework *fw); +char *smccc_pci_conduit_name(struct smccc_test_arg *arg); +int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg *arg); + +int smccc_init(fwts_framework *fw) { if (fwts_module_load(fw, module_name) != FWTS_OK) { module_loaded = false; @@ -121,7 +126,7 @@ static int smccc_init(fwts_framework *fw) return FWTS_OK; } -static int smccc_deinit(fwts_framework *fw) +int smccc_deinit(fwts_framework *fw) { if (smccc_fd >= 0) { close(smccc_fd); @@ -140,7 +145,7 @@ static int smccc_deinit(fwts_framework *fw) * smccc_pci_conduit_name() * map the conduit number to human readable string */ -static char *smccc_pci_conduit_name(struct smccc_test_arg *arg) +char *smccc_pci_conduit_name(struct smccc_test_arg *arg) { static char unknown[32]; @@ -163,7 +168,7 @@ static char *smccc_pci_conduit_name(struct smccc_test_arg *arg) * smccc_pci_conduit_check() * check if conduit number is valid */ -static int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg *arg) +int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg *arg) { switch (arg->conduit) { case FWTS_SMCCC_CONDUIT_HVC: Please move the shared part of the function to the src/lib/src directory to make it a library function instead of changing its scope limited, especially for the init and deinit functions. Cheers, Ivan -- 2.34.1 IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
Hi Edhay, Thank for the V2 patch. Looks good to me, just one thing, The patch exposes smccc_init(), smccc_deinit(), smccc_pci_conduit_name(), smccc_pci_conduit_check(), and smccc_fd by just dropping static and sprinkling extern declarations in the PSCI test. Please move this helper logic into a shared module, e.g. add fwts_smccc.[ch] under src/lib/src, and include that header from both smccc.c and psci.c. That way the common code lives in one library module and any other SMCCC test can reuse it without redeclaring prototypes. Cheers, Ivan On Sat, Oct 25, 2025 at 5:15 AM G Edhaya Chandran <Edhaya.Chandran@arm.com> wrote: > Hi Ivan, > > > > Thank you for the review comments. > > Apologies for the alignment issues, there was an issue with my editor. > > > > Attached to the mail is reworked patch. > > > > With Warm Regards, > > Edhay > > > > > > *From:* Ivan Hu <ivan.hu@canonical.com> > *Sent:* 18 September 2025 01:34 > *To:* G Edhaya Chandran <Edhaya.Chandran@arm.com> > *Cc:* fwts-devel@lists.ubuntu.com > *Subject:* Re: [PATCH 1/1] pci/smccc: New tests for Arm PSCI functions > > > > Thanks for the patch! > > please see my comment below, > > > > On Tue, Sep 16, 2025 at 3:14 AM G Edhaya Chandran <edhaya.chandran@arm.com> > wrote: > > From: edhay <edhaya.chandran@arm.com> > > Power State Coordination Interface (PSCI) is a standard to manage power > states in systems > based on Arm architectures—especially in multi-core or multi-cluster > environments. > PSCI provides a firmware interface that allows operating systems to control > the power states of CPUs and other system components in a standardized way. > Actual implementation of these calls reside in secure firmware like Arm > Trusted Firmware. > > Arm defines these interfaces in > Arm Power State Coordination Interface specification > https://developer.arm.com/documentation/den0022/latest/ > > Following new tests are added: > 1. Check if PSCI is supported as per ACPI FADT table > 2. Test for PSCI_VERSION > 3. Test for PSCI_FEATURES: Check for the implementation of all > the 22 PSCI functions > 4. Test for AFFINITY_INFO > 5. psci_features_bbr_check: Checks if all the mandatory PSCI functions > according to the Arm BBR specification is implemented. > enabled only with --sbbr or --ebbr flags > > This suite has dependency on smccc_test kernel module and the same > should loaded > > usage > sudo fwts psci > sudo fwts psci --sbbr > sudo fwts psci --ebbr > --- > smccc_test/smccc_test.c | 63 +++++ > smccc_test/smccc_test.h | 6 + > src/Makefile.am | 1 + > src/pci/smccc/psci.c | 560 ++++++++++++++++++++++++++++++++++++++++ > src/pci/smccc/smccc.c | 15 +- > 5 files changed, 640 insertions(+), 5 deletions(-) > create mode 100644 src/pci/smccc/psci.c > > diff --git a/smccc_test/smccc_test.c b/smccc_test/smccc_test.c > index b37691898852..c70add64fe6b 100644 > --- a/smccc_test/smccc_test.c > +++ b/smccc_test/smccc_test.c > @@ -85,6 +85,18 @@ MODULE_LICENSE("GPL"); > #define SMCCC_ARCH_SOC_ID 0x80000002 > #endif > > +#ifndef PSCI_VERSION > +#define PSCI_VERSION 0x84000000 > +#endif > + > +#ifndef PSCI_FEATURES > +#define PSCI_FEATURES 0x8400000A > +#endif > + > +#ifndef AFFINITY_INFO > +#define AFFINITY_INFO 0x84000004 > +#endif > + > /* > * smccc_test_copy_to_user() > * copy arm_res a* registers to user space test_arg w array > @@ -199,6 +211,51 @@ static long smccc_test_arch_soc_id(unsigned long arg) > return smccc_test_copy_to_user(arg, &arm_res, conduit); > } > > +static long smccc_test_psci_version(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(PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, > &arm_res); > + > + return smccc_test_copy_to_user(arg, &arm_res, conduit); > +} > + > +static long smccc_test_psci_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(PSCI_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_affinity_info(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(AFFINITY_INFO, test_arg.w[1], > test_arg.w[2], 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) > { > @@ -229,6 +286,12 @@ static long smccc_test_ioctl(struct file *file, > unsigned int cmd, > return smccc_test_arch_features(arg); > case SMCCC_TEST_ARCH_SOC_ID: > return smccc_test_arch_soc_id(arg); > + case SMCCC_TEST_PSCI_VERSION: > + return smccc_test_psci_version(arg); > + case SMCCC_TEST_PSCI_FEATURES: > + return smccc_test_psci_features(arg); > + case SMCCC_TEST_AFFINITY_INFO: > + return smccc_test_affinity_info(arg); > default: > break; > } > diff --git a/smccc_test/smccc_test.h b/smccc_test/smccc_test.h > index 6597d87e7b20..25c83a66e574 100644 > --- a/smccc_test/smccc_test.h > +++ b/smccc_test/smccc_test.h > @@ -45,4 +45,10 @@ struct smccc_test_arg { > _IOWR('p', 0x07, struct smccc_test_arg) > #define SMCCC_TEST_ARCH_SOC_ID \ > _IOWR('p', 0x08, struct smccc_test_arg) > +#define SMCCC_TEST_PSCI_VERSION \ > + _IOWR('p', 0x09, struct smccc_test_arg) > +#define SMCCC_TEST_PSCI_FEATURES \ > + _IOWR('p', 0x0A, struct smccc_test_arg) > +#define SMCCC_TEST_AFFINITY_INFO \ > + _IOWR('p', 0x0B, struct smccc_test_arg) > #endif > diff --git a/src/Makefile.am b/src/Makefile.am > index 2cdbe60f67e3..448318be3475 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -209,6 +209,7 @@ fwts_SOURCES = main.c \ > pci/crs/crs.c \ > pci/maxreadreq/maxreadreq.c \ > pci/smccc/smccc.c \ > + pci/smccc/psci.c \ > > > > please sync up with latest source of fwts version, smccc has been move out > from pci > > > > tpm/tpmevlog/tpmevlog.c \ > tpm/tpmevlogdump/tpmevlogdump.c \ > uefi/csm/csm.c \ > diff --git a/src/pci/smccc/psci.c b/src/pci/smccc/psci.c > new file mode 100644 > index 000000000000..feb1b8179694 > --- /dev/null > +++ b/src/pci/smccc/psci.c > @@ -0,0 +1,560 @@ > +/* > + * > + * Copyright (C) 2025 Canonical > + * Copyright (C) 2025 Arm > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA > 02110-1301, USA. > + * > + */ > +#include "fwts.h" > + > +#ifdef FWTS_ARCH_AARCH64 > + > +#include <errno.h> > +#include <inttypes.h> > +#include <stdio.h> > +#include <stddef.h> > +#include <stdbool.h> > +#include <string.h> > +#include <sys/ioctl.h> > +#include <fcntl.h> > + > +#include "smccc_test.h" > + > +/* > + * ARM PSCI tests, > + * https://developer.arm.com/documentation/den0022/latest/ > + */ > + > +extern int smccc_init(fwts_framework *fw); > +extern char *smccc_pci_conduit_name(struct smccc_test_arg *arg); > +extern int smccc_deinit(fwts_framework *fw); > +extern int smccc_pci_conduit_check(fwts_framework *fw, struct > smccc_test_arg *arg); > +extern int smccc_fd; > + > +#define PSCI_VERSION 0x84000000 > +#define CPU_SUSPEND 0x84000001 > +#define CPU_OFF 0x84000002 > +#define CPU_ON 0x84000003 > +#define AFFINITY_INFO 0x84000004 > +#define MIGRATE 0x84000005 > +#define MIGRATE_INFO_TYPE 0x84000006 > +#define MIGRATE_INFO_UP_CPU 0x84000007 > +#define SYSTEM_OFF 0x84000008 > +#define SYSTEM_RESET 0x84000009 > +#define PSCI_FEATURES 0x8400000A > +#define CPU_FREEZE 0x8400000B > +#define CPU_DEFAULT_SUSPEND 0x8400000C > +#define NODE_HW_STATE 0x8400000D > +#define SYSTEM_SUSPEND 0x8400000E > +#define PSCI_SET_SUSPEND_MODE 0x8400000F > +#define PSCI_STAT_RESIDENCY 0x84000010 > +#define PSCI_STAT_COUNT 0x84000011 > +#define SYSTEM_RESET2 0x84000012 > +#define MEM_PROTECT 0x84000013 > +#define MEM_PROTECT_CHECK_RANGE 0x84000014 > +#define SYSTEM_OFF2 0x84000015 > > > minor, please align the above data > > > + > +#define PSCI_VERSION_0_2 0x00002 > +#define PSCI_VERSION_1_0 0x10000 > +#define PSCI_VERSION_1_1 0x10001 > +#define PSCI_VERSION_1_2 0x10002 > + > +enum PSCI_ret_code { > + PSCI_SUCCESS = 0, > + PSCI_NOT_SUPPORTED = -1 > +}; > + > +typedef struct { > + const uint32_t PSCI_func_id; > + const char *PSCI_func_id_name; > + bool implemented; > +} PSCI_func_id_t; > + > +static PSCI_func_id_t PSCI_func_id_list[] = { > + { PSCI_VERSION, "PSCI_VERSION", > false }, > + { CPU_SUSPEND, "CPU_SUSPEND", > false }, > + { CPU_OFF, "CPU_OFF", > false }, > + { CPU_ON, "CPU_ON", > false }, > + { AFFINITY_INFO, "AFFINITY_INFO", > false }, > + { MIGRATE, "MIGRATE", > false }, > + { MIGRATE_INFO_TYPE, "MIGRATE_INFO_TYPE", > false }, > + { MIGRATE_INFO_UP_CPU, "MIGRATE_INFO_UP_CPU ", false > }, > + { SYSTEM_OFF, "SYSTEM_OFF", > false }, > + { SYSTEM_RESET, "SYSTEM_RESET", > false }, > + { PSCI_FEATURES, "PSCI_FEATURES", > false }, > + { CPU_FREEZE, "CPU_FREEZE", > false }, > + { CPU_DEFAULT_SUSPEND, "CPU_DEFAULT_SUSPEND", > false }, > + { NODE_HW_STATE, "NODE_HW_STATE", > false }, > + { SYSTEM_SUSPEND, "SYSTEM_SUSPEND", > false }, > + { PSCI_SET_SUSPEND_MODE, "PSCI_SET_SUSPEND_MODE", false }, > + { PSCI_STAT_RESIDENCY, "PSCI_STAT_RESIDENCY", > false }, > + { PSCI_STAT_COUNT, "PSCI_STAT_COUNT", > false }, > + { SYSTEM_RESET2, "SYSTEM_RESET2", > false }, > + { MEM_PROTECT, "MEM_PROTECT", > false }, > + { MEM_PROTECT_CHECK_RANGE, "MEM_PROTECT_CHECK_RANGE", false }, > + { SYSTEM_OFF2, "SYSTEM_OFF2", > false } > +}; > > > minor, please align the above data > > > + > +static uint16_t psci_major_version = 0; > +static uint16_t psci_minor_version = 0; > +static uint32_t psci_version = 0; > + > +/* > + * check_for_psci_support() > + * This test checks if PSCI is supported in the system by checking > the FADT table > + */ > +static int check_for_psci_support(fwts_framework *fw) > +{ > + static const fwts_acpi_gas null_gas; > + fwts_acpi_table_info *table = NULL; > + const fwts_acpi_table_fadt *fadt; > + int fadt_size; > + bool passed = true; > + > + if (fwts_acpi_find_table(fw, "FACP", 0, &table) != FWTS_OK) { > + fwts_log_error(fw, "Cannot read ACPI table FACP."); > + return FWTS_ERROR; > + } > + if (table == NULL) { > + fwts_log_error(fw, "ACPI table FACP does not exist!"); > + return FWTS_ERROR; > + } > + > + fadt = (const fwts_acpi_table_fadt *)table->data; > + fadt_size = table->length; > + if (fadt_size == 0) { > + fwts_log_error(fw, "ACPI table FACP has zero length!"); > + return FWTS_ERROR; > + } > + > + bool PSCI_compliant = fadt->arm_boot_flags & > FWTS_FACP_ARM_BOOT_ARCH_PSCI_COMPLIANT; > + if ( fadt->arm_boot_flags & FWTS_FACP_ARM_BOOT_ARCH_PSCI_COMPLIANT) { > > > bool PSCI_compliant seems not necessary, just > if (PSCI_compliant) { > > + fwts_log_info_verbatim(fw, "Arm PSCI: FADT ARM_BOOT_ARCH PSCI > compliant flag is set"); > + } > + else { > > > Minor, > Please use the coding style for the else statement's braces, placing the > opening brace on the same line as the keyword. > i.e. } else { > > > + fwts_log_error(fw, "Arm PSCI: FADT ARM_BOOT_ARCH PSCI compliant > flag is not set." > + " PSCI is not supported"); > + return FWTS_ERROR; > + } > + > + bool HVC_Conduit = fadt->arm_boot_flags & > FWTS_FACP_ARM_BOOT_ARCH_PSCI_USE_HVC; > + if (HVC_Conduit) { > > > same here > > > + fwts_log_info_verbatim(fw, "Arm PSCI: PSCI uses HVC conduit"); > + } > + else { > > > same here > > > > + fwts_log_info_verbatim(fw, "Arm PSCI: PSCI uses SMC conduit"); > + } > + > + if (memcmp((const void *)&fadt->sleep_control_reg, > + (const void *)&null_gas, > + sizeof(fwts_acpi_gas))) { > + passed = false; > > alignment? > > + fwts_log_error(fw, "Arm PSCI: FADT SLEEP_CONTROL_REG is > not zeroed (Recommended)" > + ". Non-zero general register structure detected. Must be > coded as all zeros"); > + } > > + > + if (memcmp((const void *)&fadt->sleep_status_reg, > + (const void *)&null_gas, > + sizeof(fwts_acpi_gas))) { > + passed = false; > > alignment? > > + fwts_log_error(fw, "Arm PSCI: FADT SLEEP_STATUS_REG is not > zeroed (Recommended)" > + ". Non-zero general register structure detected. Must be > coded as all zeros"); > + } > + > + if (passed == false) > + { > + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SleepNonZero", > + "FADT SLEEP_* registers field are non-zero."); > + return (FWTS_ERROR); > + } > + > + fwts_passed(fw, "Check for Arm PSCI support test passed"); > + return FWTS_OK; > +} > + > + > +/* > + * psci_version_test() > + * test PSCI function VERSION > + */ > +static int psci_version_test(fwts_framework *fw) > +{ > + int ret; > + struct smccc_test_arg arg = { }; > + > + arg.size = sizeof(arg); > + arg.w[0] = PSCI_VERSION; > + > + ret = ioctl(smccc_fd, SMCCC_TEST_PSCI_VERSION, &arg); > + if (ret < 0) { > + fwts_log_error(fw, "SMCCC test driver ioctl > PSCI_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')", > > > > PRIx32 instead of x? > > > > + arg.conduit, smccc_pci_conduit_name(&arg)); > + > + psci_major_version = arg.w[0] >> 16; > + psci_minor_version = arg.w[0] & 0x0000ffff; > + fwts_log_info_verbatim(fw, "Arm PSCI: Major version: %d, Minor > version: %d", > + psci_major_version, psci_minor_version); > > > > PRIu16 instead of d? > > > + > + psci_version = (psci_major_version << 16) + psci_minor_version; > + > + fwts_passed(fw, "Arm PSCI_VERSION test passed"); > + return FWTS_OK; > +} > + > + > +/* > + * get_implementation_status() > + * Query the implementation status by function_id > +*/ > +static bool get_implementation_status(const uint32_t func_id) > +{ > + int k=0; > + for (k=0; k<FWTS_ARRAY_SIZE(PSCI_func_id_list); k++) { > > > > please add spacing around the less than operator (<) > i.e. > + for (k=0; k < FWTS_ARRAY_SIZE(PSCI_func_id_list); k++) { > > + if (PSCI_func_id_list[k].PSCI_func_id == func_id) > + return (PSCI_func_id_list[k].implemented); > + } > + > + return false; > +} > + > +/* > + * psci_features_bbr_check() > + * Asserts based on the BBR specification rules in > + * Section 4.2 which refers to the table 6.9 in the > + * PSCI specification > https://developer.arm.com/documentation/den0022/latest/ > + * This function can be run only if SBBR or EBBR flag is set > + * while running FWTS > + */ > + > > > > extra empty line > > > > + static int psci_features_bbr_check(fwts_framework *fw) > + { > + bool bbr_check_failed = false; > + uint16_t assert_failed_count = 0; > + int i=0; > + > + fwts_log_info_verbatim(fw, "Arm BBR PSCI implementation check - > the detected PSCI version is %d.%d", > + psci_major_version, psci_minor_version); > > > > PRIu16? > > > > + > + for (i=0; i<FWTS_ARRAY_SIZE(PSCI_func_id_list); i++) { > > > add spacing around the less than operator (<) > > > + if(PSCI_func_id_list[i].implemented == false) { > + switch (PSCI_func_id_list[i].PSCI_func_id) { > + > + case PSCI_VERSION: > + case CPU_SUSPEND: > > alignment? > > > > + case CPU_OFF: > + case CPU_ON: > + case AFFINITY_INFO: > + case SYSTEM_OFF: > + case SYSTEM_RESET: { > + /* These PSCI functions are mandatory for all > versions */ > + bbr_check_failed = true; > + //fwts_log_info_verbatim(fw, "failed: The mandatory > function: %s is not implemented", > + // PSCI_func_id_list[i].PSCI_func_id_name); > > > > Could you please remove the code above? It appears to be redundant. > > > + } > + break; > + > + case PSCI_FEATURES: { > + /* PSCI_FEATURES is mandatory for > all PSCI versions >= 1.0 */ > + if(psci_version >= PSCI_VERSION_1_0) > > please add spacing after "if" > > + { > + bbr_check_failed = true; > + } > + } > + break; > + > + default: { > + fwts_log_info_verbatim(fw, "Info: > \"Optional\" Function: 0x%8.8" PRIx32 " (%s) is not Implemented.", > + > PSCI_func_id_list[i].PSCI_func_id, > + > PSCI_func_id_list[i].PSCI_func_id_name); > + } > + } > + if(bbr_check_failed == true) { > > please add spacing after "if" > > + fwts_log_error(fw, "failed: As per Arm BBR > specifciation, " > + "the implementation of %s is > \"Mandatory\" in PSCI v%d.%d but is not implemented" > + , > PSCI_func_id_list[i].PSCI_func_id_name > + , psci_major_version, > psci_minor_version); > + bbr_check_failed = false; > + > + assert_failed_count++; > + } > + } > + > + /* Additional checks */ > + /* Some PSCI functions are mandatory conditionally. Such cases > are checked below*/ > + if(PSCI_func_id_list[i].implemented == true) { > > > > please add spacing after "if" > > > > + switch (PSCI_func_id_list[i].PSCI_func_id) { > + case MIGRATE: { > + /* If MIGRATE is implemented then MIGRATE_INFO_UP_CPU > is mandatory*/ > + if (false == > get_implementation_status(MIGRATE_INFO_UP_CPU)) { > + fwts_log_error(fw, "failed: MIGRATE is > implemented however MIGRATE_INFO_UP_CPU" > + " is not implemented"); > + assert_failed_count++; > + } > + } > + break; > + > + case PSCI_STAT_RESIDENCY: { > + /* If PSCI_STAT_RESIDENCY is implemented then > PSCI_STAT_COUNT is mandatory*/ > > spacing before */ > > + if (false == > get_implementation_status(PSCI_STAT_COUNT)) { > + fwts_log_error(fw, "failed: PSCI_STAT_RESIDENCY > is implemented however PSCI_STAT_COUNT" > + " is not implemented"); > + assert_failed_count++; > + } > + } > + break; > + > + case PSCI_STAT_COUNT: { > + /* If PSCI_STAT_COUNT is implemented then > PSCI_STAT_RESIDENCY is mandatory*/ > > > > spacing before */ > > > > + if (false == > get_implementation_status(PSCI_STAT_RESIDENCY)) { > + fwts_log_error(fw, "failed: PSCI_STAT_COUNT is > implemented however PSCI_STAT_RESIDENCY" > + " is not implemented"); > + assert_failed_count++; > + } > + } > + break; > + > + case MEM_PROTECT: { > + /* If MEM_PROTECT is implemented then > MEM_PROTECT_CHECK_RANGE is mandatory*/ > > > > same here > > > > + if (false == > get_implementation_status(MEM_PROTECT_CHECK_RANGE)) { > + fwts_log_error(fw, "failed: MEM_PROTECT is > implemented however MEM_PROTECT_CHECK_RANGE" > + " is not implemented"); > + assert_failed_count++; > + } > + } > + break; > + > + case MEM_PROTECT_CHECK_RANGE: { > + /* If MEM_PROTECT_CHECK_RANGE is implemented then > MEM_PROTECT is mandatory*/ > > > > same > > > > + if (false == get_implementation_status(MEM_PROTECT)) { > + fwts_log_error(fw, "failed: > MEM_PROTECT_CHECK_RANGE is implemented however MEM_PROTECT" > + " is not implemented"); > + assert_failed_count++; > + } > + } > + break; > + } > + > + } > + } > > > > please align "}" > > > > + > + if (assert_failed_count > 0) { > + fwts_log_error(fw, "Arm SBBR check for Arm PSCI_FEATURES > failed. %d checks failed", > + assert_failed_count); > > alignment? > > + return FWTS_ERROR; > + } > + > + fwts_log_info_verbatim(fw, "Arm SBBR check for Arm PSCI_FEATURES > test passed"); > + return FWTS_OK; > + } > + > + > +/* > + * psci_features_test() > + * test PSCI function PSCI_FEATURES for each PSCI function > + */ > + > > empty line? > > + static int psci_features_test(fwts_framework *fw) > + { > + int ret; > + int i=0; > > add spacing around "=" > > + struct smccc_test_arg arg = { }; > + bool feaatures_test_failed = 0; > + > + if (psci_version == PSCI_VERSION_0_2) { > + fwts_skipped(fw, "PSCI_FEATURES is Not Applicable for PSCI > version %d.%d", > + psci_major_version, psci_minor_version); > > PRIu? > > + return FWTS_SKIP; > + } > + > + fwts_log_info_verbatim(fw, "========================= PSCI_FEATURES > query ==========================="); > + fwts_log_info_verbatim(fw, " # Function Name > Function ID Implementation Status"); > + for (i=0; i<FWTS_ARRAY_SIZE(PSCI_func_id_list); i++) { > + memset(&arg, 0, sizeof(arg)); > + arg.size = sizeof(arg); > + arg.w[0] = PSCI_FEATURES; > + > + //arg.w[1] should contain, the function id of the > + //PSCI function to query about, > > > > please use /* */ instead of // > > > > + arg.w[1] = PSCI_func_id_list[i].PSCI_func_id; > + > + ret = ioctl(smccc_fd, SMCCC_TEST_PSCI_FEATURES, &arg); > + if (ret < 0) { > + fwts_log_error(fw, "SMCCC test driver ioctl > PSCI_FEATURES " > + "failed, ret=%d, errno=%d (%s)\n", ret, > 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 == PSCI_NOT_SUPPORTED) { > + fwts_log_info_verbatim(fw, "%2d %-24s > 0x%8.8" PRIx32" NOT IMPLEMENTED", > + i+1, > + PSCI_func_id_list[i].PSCI_func_id_name, > + PSCI_func_id_list[i].PSCI_func_id); > > allignment? > > + PSCI_func_id_list[i].implemented = false; > + } > + else if (((arg.w[0] >> 31) & 0x1) == 0) { /* if bit 31 is > 0, then the function is supported */ > > > > } else if (((arg.w[0] >> 31) & 0x1) == 0) { /* if bit 31 is 0, then the > function is supported */ > > > > + fwts_log_info_verbatim(fw, "%2d %-24s > 0x%8.8" PRIx32" IMPLEMENTED", > + i+1, > + PSCI_func_id_list[i].PSCI_func_id_name, > + PSCI_func_id_list[i].PSCI_func_id); > > > > allignment? > > > > + PSCI_func_id_list[i].implemented = true; > + } > + else if (((arg.w[0] >> 31) & 0x1) == 1) { /* if bit 31 is > 1, then the implementation is incorrect as per spec*/ > > > > } else if > > spaceing before */ > > > > + fwts_log_info_verbatim(fw, "%2d %-24s 0x%8.8" > PRIx32" " > + " Error Invalid return - Please check the > implementation", > + i+1, > + PSCI_func_id_list[i].PSCI_func_id_name, > + PSCI_func_id_list[i].PSCI_func_id); > + PSCI_func_id_list[i].implemented = false; > + feaatures_test_failed = 1; > + } > + } > + fwts_log_info_verbatim(fw, "\n"); > > > > use fwts_log_nl(fw); for a new line > > > > + //if --sbbr flag or --ebbr is set, additionally call the BBR > assertions function > > > > /* */ > > > > + if (fw->flags & FWTS_FLAG_SBBR || fw->flags & FWTS_FLAG_EBBR) { > + if (psci_features_bbr_check(fw) == FWTS_ERROR) { > + fwts_failed(fw, LOG_LEVEL_HIGH, > "psci_features_bbr_check check ", > + "for Arm BBR specification rules > failed.\n"); > + return (FWTS_ERROR); > + } > > alignment? > > + } > + > + if(feaatures_test_failed == 1) { > + fwts_failed(fw, LOG_LEVEL_HIGH, "Arm Arm PSCI_FEATURES test", > "failed"); > + return FWTS_ERROR; > + } > + > + fwts_passed(fw, "Arm PSCI_FEATURES test passed"); > + return FWTS_OK; > + } > + > + > + > > extra empty line? > > +static inline uint64_t read_mpidr_el1(void) > +{ > + uint64_t mpidr; > + asm volatile("mrs %0, mpidr_el1" : "=r" (mpidr)); > + return mpidr; > +} > + > +static int affinity_info_test(fwts_framework *fw) > +{ > + int ret; > + struct smccc_test_arg arg = { }; > + > > > > alignment? > > > > + uint64_t mpidr = read_mpidr_el1(); > + fwts_log_info_verbatim(fw, "Info: mpidr = 0x%16.16" PRIx64"", mpidr); > + > + > + uint32_t aff0 = mpidr & 0xFF; > + uint32_t aff1 = (mpidr >> 8) & 0xFF; > + uint32_t aff2 = (mpidr >> 16) & 0xFF; > + /* aff3 is unused */ > + > + uint32_t target_affinity = 0; > + target_affinity |= aff0; > + target_affinity |= (aff1 << 8); > + target_affinity |= (aff2 << 16); > + > + fwts_log_info_verbatim(fw, "Info: target_affinity = 0x%8.8" > PRIx32"", target_affinity); > + > > alignment? > > > > + uint32_t lowest_affinity_level = 0; /* All affinity levels are valid > */ > + > + arg.size = sizeof(arg); > + arg.w[0] = AFFINITY_INFO; > + arg.w[1] = target_affinity; > + arg.w[2] = lowest_affinity_level; > + > + ret = ioctl(smccc_fd, SMCCC_TEST_AFFINITY_INFO, &arg); > + if (ret < 0) { > + fwts_log_error(fw, "SMCCC test driver ioctl > SMCCC_TEST_AFFINITY_INFO " > + "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 result = arg.w[0]; > + switch(result) { > + case 2: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d (ON_PENDING)", > result); > + } > + break; > + case 1: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d (OFF)", result); > + } > + break; > + case 0: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d (ON)", result); > + } > + break; > + case -2: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d > (INVALID_PARAMETERS)", result); > + } > + break; > + case -8: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d (DISABLED)", > result); > + } > + break; > + default: { > + fwts_log_info_verbatim(fw, > + "Test AFFINITY_INFO returned value = %d , an unexpected > value", result); > + fwts_failed(fw, LOG_LEVEL_HIGH, "Arm PSCI AFFINITY_INFO ", > + "test failed"); > + return FWTS_ERROR; > + } > + } > + > + fwts_passed(fw, "Arm PSCI AFFINITY_INFO test passed"); > + return FWTS_OK; > +} > + > +static fwts_framework_minor_test psci_tests[] = { > + { check_for_psci_support, "Check for Arm PSCI support" }, > + { psci_version_test, "Test PSCI_VERSION" }, > + { psci_features_test, "Test PSCI_FEATURES" }, > + { affinity_info_test, "Test AFFINITY_INFO" }, > + { NULL, NULL } > +}; > > > > alignment? > > > > + > +static fwts_framework_ops psciops = { > + .description = "ARM64 PSCI tests.", > + .init = smccc_init, > + .deinit = smccc_deinit, > + .minor_tests = psci_tests > +}; > + > +FWTS_REGISTER("psci", &psciops, FWTS_TEST_ANYTIME, FWTS_FLAG_UNSAFE | > FWTS_FLAG_ROOT_PRIV) > + > + #endif > diff --git a/src/pci/smccc/smccc.c b/src/pci/smccc/smccc.c > index 144a0a24839b..fc477f2417b8 100644 > --- a/src/pci/smccc/smccc.c > +++ b/src/pci/smccc/smccc.c > @@ -84,7 +84,7 @@ static pci_func_id_t pci_func_ids[] = { > static const char *module_name = "smccc_test"; > static const char *dev_name = "/dev/smccc_test"; > static bool module_loaded; > -static int smccc_fd = -1; > +int smccc_fd = -1; > > typedef struct { > const uint32_t SMCCC_func_id; > @@ -103,7 +103,12 @@ static SMCCC_func_id_t SMCCC_func_id_list[] = { > static uint16_t smccc_major_version = 0; > static uint16_t smccc_minor_version = 0; > > -static int smccc_init(fwts_framework *fw) > +int smccc_init(fwts_framework *fw); > +int smccc_deinit(fwts_framework *fw); > +char *smccc_pci_conduit_name(struct smccc_test_arg *arg); > +int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg > *arg); > + > +int smccc_init(fwts_framework *fw) > { > if (fwts_module_load(fw, module_name) != FWTS_OK) { > module_loaded = false; > @@ -121,7 +126,7 @@ static int smccc_init(fwts_framework *fw) > return FWTS_OK; > } > > -static int smccc_deinit(fwts_framework *fw) > +int smccc_deinit(fwts_framework *fw) > { > if (smccc_fd >= 0) { > close(smccc_fd); > @@ -140,7 +145,7 @@ static int smccc_deinit(fwts_framework *fw) > * smccc_pci_conduit_name() > * map the conduit number to human readable string > */ > -static char *smccc_pci_conduit_name(struct smccc_test_arg *arg) > +char *smccc_pci_conduit_name(struct smccc_test_arg *arg) > { > static char unknown[32]; > > @@ -163,7 +168,7 @@ static char *smccc_pci_conduit_name(struct > smccc_test_arg *arg) > * smccc_pci_conduit_check() > * check if conduit number is valid > */ > -static int smccc_pci_conduit_check(fwts_framework *fw, struct > smccc_test_arg *arg) > +int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg > *arg) > { > switch (arg->conduit) { > case FWTS_SMCCC_CONDUIT_HVC: > > > > Please move the shared part of the function to the src/lib/src directory > to make it a library function instead of changing its scope limited, > especially for the init and deinit functions. > > Cheers, > Ivan > > > > -- > 2.34.1 > > IMPORTANT NOTICE: The contents of this email and any attachments are > confidential and may also be privileged. If you are not the intended > recipient, please notify the sender immediately and do not disclose the > contents to any other person, use it for any purpose, or store or copy the > information in any medium. Thank you. >
diff --git a/smccc_test/smccc_test.c b/smccc_test/smccc_test.c index b37691898852..c70add64fe6b 100644 --- a/smccc_test/smccc_test.c +++ b/smccc_test/smccc_test.c @@ -85,6 +85,18 @@ MODULE_LICENSE("GPL"); #define SMCCC_ARCH_SOC_ID 0x80000002 #endif +#ifndef PSCI_VERSION +#define PSCI_VERSION 0x84000000 +#endif + +#ifndef PSCI_FEATURES +#define PSCI_FEATURES 0x8400000A +#endif + +#ifndef AFFINITY_INFO +#define AFFINITY_INFO 0x84000004 +#endif + /* * smccc_test_copy_to_user() * copy arm_res a* registers to user space test_arg w array @@ -199,6 +211,51 @@ static long smccc_test_arch_soc_id(unsigned long arg) return smccc_test_copy_to_user(arg, &arm_res, conduit); } +static long smccc_test_psci_version(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(PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &arm_res); + + return smccc_test_copy_to_user(arg, &arm_res, conduit); +} + +static long smccc_test_psci_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(PSCI_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_affinity_info(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(AFFINITY_INFO, test_arg.w[1], test_arg.w[2], 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) { @@ -229,6 +286,12 @@ static long smccc_test_ioctl(struct file *file, unsigned int cmd, return smccc_test_arch_features(arg); case SMCCC_TEST_ARCH_SOC_ID: return smccc_test_arch_soc_id(arg); + case SMCCC_TEST_PSCI_VERSION: + return smccc_test_psci_version(arg); + case SMCCC_TEST_PSCI_FEATURES: + return smccc_test_psci_features(arg); + case SMCCC_TEST_AFFINITY_INFO: + return smccc_test_affinity_info(arg); default: break; } diff --git a/smccc_test/smccc_test.h b/smccc_test/smccc_test.h index 6597d87e7b20..25c83a66e574 100644 --- a/smccc_test/smccc_test.h +++ b/smccc_test/smccc_test.h @@ -45,4 +45,10 @@ struct smccc_test_arg { _IOWR('p', 0x07, struct smccc_test_arg) #define SMCCC_TEST_ARCH_SOC_ID \ _IOWR('p', 0x08, struct smccc_test_arg) +#define SMCCC_TEST_PSCI_VERSION \ + _IOWR('p', 0x09, struct smccc_test_arg) +#define SMCCC_TEST_PSCI_FEATURES \ + _IOWR('p', 0x0A, struct smccc_test_arg) +#define SMCCC_TEST_AFFINITY_INFO \ + _IOWR('p', 0x0B, struct smccc_test_arg) #endif diff --git a/src/Makefile.am b/src/Makefile.am index 2cdbe60f67e3..448318be3475 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -209,6 +209,7 @@ fwts_SOURCES = main.c \ pci/crs/crs.c \ pci/maxreadreq/maxreadreq.c \ pci/smccc/smccc.c \ + pci/smccc/psci.c \ tpm/tpmevlog/tpmevlog.c \ tpm/tpmevlogdump/tpmevlogdump.c \ uefi/csm/csm.c \ diff --git a/src/pci/smccc/psci.c b/src/pci/smccc/psci.c new file mode 100644 index 000000000000..feb1b8179694 --- /dev/null +++ b/src/pci/smccc/psci.c @@ -0,0 +1,560 @@ +/* + * + * Copyright (C) 2025 Canonical + * Copyright (C) 2025 Arm + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "fwts.h" + +#ifdef FWTS_ARCH_AARCH64 + +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stddef.h> +#include <stdbool.h> +#include <string.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#include "smccc_test.h" + +/* + * ARM PSCI tests, + * https://developer.arm.com/documentation/den0022/latest/ + */ + +extern int smccc_init(fwts_framework *fw); +extern char *smccc_pci_conduit_name(struct smccc_test_arg *arg); +extern int smccc_deinit(fwts_framework *fw); +extern int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg *arg); +extern int smccc_fd; + +#define PSCI_VERSION 0x84000000 +#define CPU_SUSPEND 0x84000001 +#define CPU_OFF 0x84000002 +#define CPU_ON 0x84000003 +#define AFFINITY_INFO 0x84000004 +#define MIGRATE 0x84000005 +#define MIGRATE_INFO_TYPE 0x84000006 +#define MIGRATE_INFO_UP_CPU 0x84000007 +#define SYSTEM_OFF 0x84000008 +#define SYSTEM_RESET 0x84000009 +#define PSCI_FEATURES 0x8400000A +#define CPU_FREEZE 0x8400000B +#define CPU_DEFAULT_SUSPEND 0x8400000C +#define NODE_HW_STATE 0x8400000D +#define SYSTEM_SUSPEND 0x8400000E +#define PSCI_SET_SUSPEND_MODE 0x8400000F +#define PSCI_STAT_RESIDENCY 0x84000010 +#define PSCI_STAT_COUNT 0x84000011 +#define SYSTEM_RESET2 0x84000012 +#define MEM_PROTECT 0x84000013 +#define MEM_PROTECT_CHECK_RANGE 0x84000014 +#define SYSTEM_OFF2 0x84000015 + +#define PSCI_VERSION_0_2 0x00002 +#define PSCI_VERSION_1_0 0x10000 +#define PSCI_VERSION_1_1 0x10001 +#define PSCI_VERSION_1_2 0x10002 + +enum PSCI_ret_code { + PSCI_SUCCESS = 0, + PSCI_NOT_SUPPORTED = -1 +}; + +typedef struct { + const uint32_t PSCI_func_id; + const char *PSCI_func_id_name; + bool implemented; +} PSCI_func_id_t; + +static PSCI_func_id_t PSCI_func_id_list[] = { + { PSCI_VERSION, "PSCI_VERSION", false }, + { CPU_SUSPEND, "CPU_SUSPEND", false }, + { CPU_OFF, "CPU_OFF", false }, + { CPU_ON, "CPU_ON", false }, + { AFFINITY_INFO, "AFFINITY_INFO", false }, + { MIGRATE, "MIGRATE", false }, + { MIGRATE_INFO_TYPE, "MIGRATE_INFO_TYPE", false }, + { MIGRATE_INFO_UP_CPU, "MIGRATE_INFO_UP_CPU ", false }, + { SYSTEM_OFF, "SYSTEM_OFF", false }, + { SYSTEM_RESET, "SYSTEM_RESET", false }, + { PSCI_FEATURES, "PSCI_FEATURES", false }, + { CPU_FREEZE, "CPU_FREEZE", false }, + { CPU_DEFAULT_SUSPEND, "CPU_DEFAULT_SUSPEND", false }, + { NODE_HW_STATE, "NODE_HW_STATE", false }, + { SYSTEM_SUSPEND, "SYSTEM_SUSPEND", false }, + { PSCI_SET_SUSPEND_MODE, "PSCI_SET_SUSPEND_MODE", false }, + { PSCI_STAT_RESIDENCY, "PSCI_STAT_RESIDENCY", false }, + { PSCI_STAT_COUNT, "PSCI_STAT_COUNT", false }, + { SYSTEM_RESET2, "SYSTEM_RESET2", false }, + { MEM_PROTECT, "MEM_PROTECT", false }, + { MEM_PROTECT_CHECK_RANGE, "MEM_PROTECT_CHECK_RANGE", false }, + { SYSTEM_OFF2, "SYSTEM_OFF2", false } +}; + +static uint16_t psci_major_version = 0; +static uint16_t psci_minor_version = 0; +static uint32_t psci_version = 0; + +/* + * check_for_psci_support() + * This test checks if PSCI is supported in the system by checking the FADT table + */ +static int check_for_psci_support(fwts_framework *fw) +{ + static const fwts_acpi_gas null_gas; + fwts_acpi_table_info *table = NULL; + const fwts_acpi_table_fadt *fadt; + int fadt_size; + bool passed = true; + + if (fwts_acpi_find_table(fw, "FACP", 0, &table) != FWTS_OK) { + fwts_log_error(fw, "Cannot read ACPI table FACP."); + return FWTS_ERROR; + } + if (table == NULL) { + fwts_log_error(fw, "ACPI table FACP does not exist!"); + return FWTS_ERROR; + } + + fadt = (const fwts_acpi_table_fadt *)table->data; + fadt_size = table->length; + if (fadt_size == 0) { + fwts_log_error(fw, "ACPI table FACP has zero length!"); + return FWTS_ERROR; + } + + bool PSCI_compliant = fadt->arm_boot_flags & FWTS_FACP_ARM_BOOT_ARCH_PSCI_COMPLIANT; + if (PSCI_compliant) { + fwts_log_info_verbatim(fw, "Arm PSCI: FADT ARM_BOOT_ARCH PSCI compliant flag is set"); + } + else { + fwts_log_error(fw, "Arm PSCI: FADT ARM_BOOT_ARCH PSCI compliant flag is not set." + " PSCI is not supported"); + return FWTS_ERROR; + } + + bool HVC_Conduit = fadt->arm_boot_flags & FWTS_FACP_ARM_BOOT_ARCH_PSCI_USE_HVC; + if (HVC_Conduit) { + fwts_log_info_verbatim(fw, "Arm PSCI: PSCI uses HVC conduit"); + } + else { + fwts_log_info_verbatim(fw, "Arm PSCI: PSCI uses SMC conduit"); + } + + if (memcmp((const void *)&fadt->sleep_control_reg, + (const void *)&null_gas, + sizeof(fwts_acpi_gas))) { + passed = false; + fwts_log_error(fw, "Arm PSCI: FADT SLEEP_CONTROL_REG is not zeroed (Recommended)" + ". Non-zero general register structure detected. Must be coded as all zeros"); + } + + if (memcmp((const void *)&fadt->sleep_status_reg, + (const void *)&null_gas, + sizeof(fwts_acpi_gas))) { + passed = false; + fwts_log_error(fw, "Arm PSCI: FADT SLEEP_STATUS_REG is not zeroed (Recommended)" + ". Non-zero general register structure detected. Must be coded as all zeros"); + } + + if (passed == false) + { + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SleepNonZero", + "FADT SLEEP_* registers field are non-zero."); + return (FWTS_ERROR); + } + + fwts_passed(fw, "Check for Arm PSCI support test passed"); + return FWTS_OK; +} + + +/* + * psci_version_test() + * test PSCI function VERSION + */ +static int psci_version_test(fwts_framework *fw) +{ + int ret; + struct smccc_test_arg arg = { }; + + arg.size = sizeof(arg); + arg.w[0] = PSCI_VERSION; + + ret = ioctl(smccc_fd, SMCCC_TEST_PSCI_VERSION, &arg); + if (ret < 0) { + fwts_log_error(fw, "SMCCC test driver ioctl PSCI_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)); + + psci_major_version = arg.w[0] >> 16; + psci_minor_version = arg.w[0] & 0x0000ffff; + fwts_log_info_verbatim(fw, "Arm PSCI: Major version: %d, Minor version: %d", + psci_major_version, psci_minor_version); + + psci_version = (psci_major_version << 16) + psci_minor_version; + + fwts_passed(fw, "Arm PSCI_VERSION test passed"); + return FWTS_OK; +} + + +/* + * get_implementation_status() + * Query the implementation status by function_id +*/ +static bool get_implementation_status(const uint32_t func_id) +{ + int k=0; + for (k=0; k<FWTS_ARRAY_SIZE(PSCI_func_id_list); k++) { + if (PSCI_func_id_list[k].PSCI_func_id == func_id) + return (PSCI_func_id_list[k].implemented); + } + + return false; +} + +/* + * psci_features_bbr_check() + * Asserts based on the BBR specification rules in + * Section 4.2 which refers to the table 6.9 in the + * PSCI specification https://developer.arm.com/documentation/den0022/latest/ + * This function can be run only if SBBR or EBBR flag is set + * while running FWTS + */ + + static int psci_features_bbr_check(fwts_framework *fw) + { + bool bbr_check_failed = false; + uint16_t assert_failed_count = 0; + int i=0; + + fwts_log_info_verbatim(fw, "Arm BBR PSCI implementation check - the detected PSCI version is %d.%d", + psci_major_version, psci_minor_version); + + for (i=0; i<FWTS_ARRAY_SIZE(PSCI_func_id_list); i++) { + if(PSCI_func_id_list[i].implemented == false) { + switch (PSCI_func_id_list[i].PSCI_func_id) { + + case PSCI_VERSION: + case CPU_SUSPEND: + case CPU_OFF: + case CPU_ON: + case AFFINITY_INFO: + case SYSTEM_OFF: + case SYSTEM_RESET: { + /* These PSCI functions are mandatory for all versions */ + bbr_check_failed = true; + //fwts_log_info_verbatim(fw, "failed: The mandatory function: %s is not implemented", + // PSCI_func_id_list[i].PSCI_func_id_name); + } + break; + + case PSCI_FEATURES: { + /* PSCI_FEATURES is mandatory for all PSCI versions >= 1.0 */ + if(psci_version >= PSCI_VERSION_1_0) + { + bbr_check_failed = true; + } + } + break; + + default: { + fwts_log_info_verbatim(fw, "Info: \"Optional\" Function: 0x%8.8" PRIx32 " (%s) is not Implemented.", + PSCI_func_id_list[i].PSCI_func_id, + PSCI_func_id_list[i].PSCI_func_id_name); + } + } + if(bbr_check_failed == true) { + fwts_log_error(fw, "failed: As per Arm BBR specifciation, " + "the implementation of %s is \"Mandatory\" in PSCI v%d.%d but is not implemented" + , PSCI_func_id_list[i].PSCI_func_id_name + , psci_major_version, psci_minor_version); + bbr_check_failed = false; + + assert_failed_count++; + } + } + + /* Additional checks */ + /* Some PSCI functions are mandatory conditionally. Such cases are checked below*/ + if(PSCI_func_id_list[i].implemented == true) { + switch (PSCI_func_id_list[i].PSCI_func_id) { + case MIGRATE: { + /* If MIGRATE is implemented then MIGRATE_INFO_UP_CPU is mandatory*/ + if (false == get_implementation_status(MIGRATE_INFO_UP_CPU)) { + fwts_log_error(fw, "failed: MIGRATE is implemented however MIGRATE_INFO_UP_CPU" + " is not implemented"); + assert_failed_count++; + } + } + break; + + case PSCI_STAT_RESIDENCY: { + /* If PSCI_STAT_RESIDENCY is implemented then PSCI_STAT_COUNT is mandatory*/ + if (false == get_implementation_status(PSCI_STAT_COUNT)) { + fwts_log_error(fw, "failed: PSCI_STAT_RESIDENCY is implemented however PSCI_STAT_COUNT" + " is not implemented"); + assert_failed_count++; + } + } + break; + + case PSCI_STAT_COUNT: { + /* If PSCI_STAT_COUNT is implemented then PSCI_STAT_RESIDENCY is mandatory*/ + if (false == get_implementation_status(PSCI_STAT_RESIDENCY)) { + fwts_log_error(fw, "failed: PSCI_STAT_COUNT is implemented however PSCI_STAT_RESIDENCY" + " is not implemented"); + assert_failed_count++; + } + } + break; + + case MEM_PROTECT: { + /* If MEM_PROTECT is implemented then MEM_PROTECT_CHECK_RANGE is mandatory*/ + if (false == get_implementation_status(MEM_PROTECT_CHECK_RANGE)) { + fwts_log_error(fw, "failed: MEM_PROTECT is implemented however MEM_PROTECT_CHECK_RANGE" + " is not implemented"); + assert_failed_count++; + } + } + break; + + case MEM_PROTECT_CHECK_RANGE: { + /* If MEM_PROTECT_CHECK_RANGE is implemented then MEM_PROTECT is mandatory*/ + if (false == get_implementation_status(MEM_PROTECT)) { + fwts_log_error(fw, "failed: MEM_PROTECT_CHECK_RANGE is implemented however MEM_PROTECT" + " is not implemented"); + assert_failed_count++; + } + } + break; + } + + } + } + + if (assert_failed_count > 0) { + fwts_log_error(fw, "Arm SBBR check for Arm PSCI_FEATURES failed. %d checks failed", + assert_failed_count); + return FWTS_ERROR; + } + + fwts_log_info_verbatim(fw, "Arm SBBR check for Arm PSCI_FEATURES test passed"); + return FWTS_OK; + } + + +/* + * psci_features_test() + * test PSCI function PSCI_FEATURES for each PSCI function + */ + + static int psci_features_test(fwts_framework *fw) + { + int ret; + int i=0; + struct smccc_test_arg arg = { }; + bool feaatures_test_failed = 0; + + if (psci_version == PSCI_VERSION_0_2) { + fwts_skipped(fw, "PSCI_FEATURES is Not Applicable for PSCI version %d.%d", + psci_major_version, psci_minor_version); + return FWTS_SKIP; + } + + fwts_log_info_verbatim(fw, "========================= PSCI_FEATURES query ==========================="); + fwts_log_info_verbatim(fw, " # Function Name Function ID Implementation Status"); + for (i=0; i<FWTS_ARRAY_SIZE(PSCI_func_id_list); i++) { + memset(&arg, 0, sizeof(arg)); + arg.size = sizeof(arg); + arg.w[0] = PSCI_FEATURES; + + //arg.w[1] should contain, the function id of the + //PSCI function to query about, + arg.w[1] = PSCI_func_id_list[i].PSCI_func_id; + + ret = ioctl(smccc_fd, SMCCC_TEST_PSCI_FEATURES, &arg); + if (ret < 0) { + fwts_log_error(fw, "SMCCC test driver ioctl PSCI_FEATURES " + "failed, ret=%d, errno=%d (%s)\n", ret, 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 == PSCI_NOT_SUPPORTED) { + fwts_log_info_verbatim(fw, "%2d %-24s 0x%8.8" PRIx32" NOT IMPLEMENTED", + i+1, + PSCI_func_id_list[i].PSCI_func_id_name, + PSCI_func_id_list[i].PSCI_func_id); + PSCI_func_id_list[i].implemented = false; + } + else if (((arg.w[0] >> 31) & 0x1) == 0) { /* if bit 31 is 0, then the function is supported */ + fwts_log_info_verbatim(fw, "%2d %-24s 0x%8.8" PRIx32" IMPLEMENTED", + i+1, + PSCI_func_id_list[i].PSCI_func_id_name, + PSCI_func_id_list[i].PSCI_func_id); + PSCI_func_id_list[i].implemented = true; + } + else if (((arg.w[0] >> 31) & 0x1) == 1) { /* if bit 31 is 1, then the implementation is incorrect as per spec*/ + fwts_log_info_verbatim(fw, "%2d %-24s 0x%8.8" PRIx32" " + " Error Invalid return - Please check the implementation", + i+1, + PSCI_func_id_list[i].PSCI_func_id_name, + PSCI_func_id_list[i].PSCI_func_id); + PSCI_func_id_list[i].implemented = false; + feaatures_test_failed = 1; + } + } + fwts_log_info_verbatim(fw, "\n"); + //if --sbbr flag or --ebbr is set, additionally call the BBR assertions function + if (fw->flags & FWTS_FLAG_SBBR || fw->flags & FWTS_FLAG_EBBR) { + if (psci_features_bbr_check(fw) == FWTS_ERROR) { + fwts_failed(fw, LOG_LEVEL_HIGH, "psci_features_bbr_check check ", + "for Arm BBR specification rules failed.\n"); + return (FWTS_ERROR); + } + } + + if(feaatures_test_failed == 1) { + fwts_failed(fw, LOG_LEVEL_HIGH, "Arm Arm PSCI_FEATURES test", "failed"); + return FWTS_ERROR; + } + + fwts_passed(fw, "Arm PSCI_FEATURES test passed"); + return FWTS_OK; + } + + + +static inline uint64_t read_mpidr_el1(void) +{ + uint64_t mpidr; + asm volatile("mrs %0, mpidr_el1" : "=r" (mpidr)); + return mpidr; +} + +static int affinity_info_test(fwts_framework *fw) +{ + int ret; + struct smccc_test_arg arg = { }; + + uint64_t mpidr = read_mpidr_el1(); + fwts_log_info_verbatim(fw, "Info: mpidr = 0x%16.16" PRIx64"", mpidr); + + + uint32_t aff0 = mpidr & 0xFF; + uint32_t aff1 = (mpidr >> 8) & 0xFF; + uint32_t aff2 = (mpidr >> 16) & 0xFF; + /* aff3 is unused */ + + uint32_t target_affinity = 0; + target_affinity |= aff0; + target_affinity |= (aff1 << 8); + target_affinity |= (aff2 << 16); + + fwts_log_info_verbatim(fw, "Info: target_affinity = 0x%8.8" PRIx32"", target_affinity); + + uint32_t lowest_affinity_level = 0; /* All affinity levels are valid */ + + arg.size = sizeof(arg); + arg.w[0] = AFFINITY_INFO; + arg.w[1] = target_affinity; + arg.w[2] = lowest_affinity_level; + + ret = ioctl(smccc_fd, SMCCC_TEST_AFFINITY_INFO, &arg); + if (ret < 0) { + fwts_log_error(fw, "SMCCC test driver ioctl SMCCC_TEST_AFFINITY_INFO " + "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 result = arg.w[0]; + switch(result) { + case 2: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d (ON_PENDING)", result); + } + break; + case 1: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d (OFF)", result); + } + break; + case 0: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d (ON)", result); + } + break; + case -2: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d (INVALID_PARAMETERS)", result); + } + break; + case -8: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d (DISABLED)", result); + } + break; + default: { + fwts_log_info_verbatim(fw, + "Test AFFINITY_INFO returned value = %d , an unexpected value", result); + fwts_failed(fw, LOG_LEVEL_HIGH, "Arm PSCI AFFINITY_INFO ", + "test failed"); + return FWTS_ERROR; + } + } + + fwts_passed(fw, "Arm PSCI AFFINITY_INFO test passed"); + return FWTS_OK; +} + +static fwts_framework_minor_test psci_tests[] = { + { check_for_psci_support, "Check for Arm PSCI support" }, + { psci_version_test, "Test PSCI_VERSION" }, + { psci_features_test, "Test PSCI_FEATURES" }, + { affinity_info_test, "Test AFFINITY_INFO" }, + { NULL, NULL } +}; + +static fwts_framework_ops psciops = { + .description = "ARM64 PSCI tests.", + .init = smccc_init, + .deinit = smccc_deinit, + .minor_tests = psci_tests +}; + +FWTS_REGISTER("psci", &psciops, FWTS_TEST_ANYTIME, FWTS_FLAG_UNSAFE | FWTS_FLAG_ROOT_PRIV) + + #endif diff --git a/src/pci/smccc/smccc.c b/src/pci/smccc/smccc.c index 144a0a24839b..fc477f2417b8 100644 --- a/src/pci/smccc/smccc.c +++ b/src/pci/smccc/smccc.c @@ -84,7 +84,7 @@ static pci_func_id_t pci_func_ids[] = { static const char *module_name = "smccc_test"; static const char *dev_name = "/dev/smccc_test"; static bool module_loaded; -static int smccc_fd = -1; +int smccc_fd = -1; typedef struct { const uint32_t SMCCC_func_id; @@ -103,7 +103,12 @@ static SMCCC_func_id_t SMCCC_func_id_list[] = { static uint16_t smccc_major_version = 0; static uint16_t smccc_minor_version = 0; -static int smccc_init(fwts_framework *fw) +int smccc_init(fwts_framework *fw); +int smccc_deinit(fwts_framework *fw); +char *smccc_pci_conduit_name(struct smccc_test_arg *arg); +int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg *arg); + +int smccc_init(fwts_framework *fw) { if (fwts_module_load(fw, module_name) != FWTS_OK) { module_loaded = false; @@ -121,7 +126,7 @@ static int smccc_init(fwts_framework *fw) return FWTS_OK; } -static int smccc_deinit(fwts_framework *fw) +int smccc_deinit(fwts_framework *fw) { if (smccc_fd >= 0) { close(smccc_fd); @@ -140,7 +145,7 @@ static int smccc_deinit(fwts_framework *fw) * smccc_pci_conduit_name() * map the conduit number to human readable string */ -static char *smccc_pci_conduit_name(struct smccc_test_arg *arg) +char *smccc_pci_conduit_name(struct smccc_test_arg *arg) { static char unknown[32]; @@ -163,7 +168,7 @@ static char *smccc_pci_conduit_name(struct smccc_test_arg *arg) * smccc_pci_conduit_check() * check if conduit number is valid */ -static int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg *arg) +int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg *arg) { switch (arg->conduit) { case FWTS_SMCCC_CONDUIT_HVC:
From: edhay <edhaya.chandran@arm.com> Power State Coordination Interface (PSCI) is a standard to manage power states in systems based on Arm architectures—especially in multi-core or multi-cluster environments. PSCI provides a firmware interface that allows operating systems to control the power states of CPUs and other system components in a standardized way. Actual implementation of these calls reside in secure firmware like Arm Trusted Firmware. Arm defines these interfaces in Arm Power State Coordination Interface specification https://developer.arm.com/documentation/den0022/latest/ Following new tests are added: 1. Check if PSCI is supported as per ACPI FADT table 2. Test for PSCI_VERSION 3. Test for PSCI_FEATURES: Check for the implementation of all the 22 PSCI functions 4. Test for AFFINITY_INFO 5. psci_features_bbr_check: Checks if all the mandatory PSCI functions according to the Arm BBR specification is implemented. enabled only with --sbbr or --ebbr flags This suite has dependency on smccc_test kernel module and the same should loaded usage sudo fwts psci sudo fwts psci --sbbr sudo fwts psci --ebbr --- smccc_test/smccc_test.c | 63 +++++ smccc_test/smccc_test.h | 6 + src/Makefile.am | 1 + src/pci/smccc/psci.c | 560 ++++++++++++++++++++++++++++++++++++++++ src/pci/smccc/smccc.c | 15 +- 5 files changed, 640 insertions(+), 5 deletions(-) create mode 100644 src/pci/smccc/psci.c