Signed-off-by: Andrew Jones <drjones@xxxxxxxxxx> --- arm/psci.c | 132 ++++++++++++++++++++++++++++++++++++++++++------------ arm/unittests.cfg | 7 +++ 2 files changed, 111 insertions(+), 28 deletions(-) diff --git a/arm/psci.c b/arm/psci.c index 5cb4d5c7c233..a092f00c2cab 100644 --- a/arm/psci.c +++ b/arm/psci.c @@ -12,6 +12,9 @@ #include <asm/processor.h> #include <asm/smp.h> #include <asm/psci.h> +#include <asm/delay.h> + +static cpumask_t halted; static bool invalid_function_exception; @@ -49,25 +52,15 @@ static bool psci_invalid_function(void) return pass; } -static int psci_affinity_info(unsigned long target_affinity, uint32_t lowest_affinity_level) +static int psci_affinity_info(unsigned int cpu) { #ifdef __arm__ - return psci_invoke(PSCI_0_2_FN_AFFINITY_INFO, target_affinity, lowest_affinity_level, 0); + return psci_invoke(PSCI_0_2_FN_AFFINITY_INFO, cpus[cpu], 0, 0); #else - return psci_invoke(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level, 0); + return psci_invoke(PSCI_0_2_FN64_AFFINITY_INFO, cpus[cpu], 0, 0); #endif } -static bool psci_affinity_info_on(void) -{ - return psci_affinity_info(cpus[0], 0) == PSCI_0_2_AFFINITY_LEVEL_ON; -} - -static bool psci_affinity_info_off(void) -{ - return psci_affinity_info(cpus[1], 0) == PSCI_0_2_AFFINITY_LEVEL_OFF; -} - static int cpu_on_ret[NR_CPUS]; static cpumask_t cpu_on_ready, cpu_on_done; static volatile int cpu_on_start; @@ -122,33 +115,116 @@ static bool psci_cpu_on_test(void) return !failed; } -int main(void) +static void general_check(void) { - int ver = psci_invoke(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); - - if (nr_cpus < 2) { - report_skip("At least 2 cpus required"); - goto done; - } - - report_info("PSCI version %d.%d", PSCI_VERSION_MAJOR(ver), - PSCI_VERSION_MINOR(ver)); report("invalid-function", psci_invalid_function()); - report("affinity-info-on", psci_affinity_info_on()); - report("affinity-info-off", psci_affinity_info_off()); + report("affinity-info-on", psci_affinity_info(0) == PSCI_0_2_AFFINITY_LEVEL_ON); + report("affinity-info-off", psci_affinity_info(1) == PSCI_0_2_AFFINITY_LEVEL_OFF); if (ERRATA(6c7a5dce22b3)) report("cpu-on", psci_cpu_on_test()); else report_skip("Skipping unsafe cpu-on test. Set ERRATA_6c7a5dce22b3=y to enable."); +} -done: +static void general_exit(void) +{ #if 0 report_summary(); psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); report("system-off", false); return 1; /* only reaches here if system-off fails */ -#else - return report_summary(); #endif } + +static void do_wfi(void *data) +{ + cpumask_set_cpu(smp_processor_id(), &halted); + wfi(); + cpumask_clear_cpu(smp_processor_id(), &halted); +} + +static void migrate_prepare(void) +{ + /* + * CPU0 is "runnable" now and should remain so after migration. + * CPU1 is "stopped" now and should remain so after migration. + * CPU2 will execute WFI before migration, so it should be "halted", both + * before and after the migration. + */ + assert(psci_affinity_info(0) == PSCI_0_2_AFFINITY_LEVEL_ON); + assert(psci_affinity_info(1) == PSCI_0_2_AFFINITY_LEVEL_OFF); + on_cpu_async(2, do_wfi, NULL); + while (!cpumask_test_cpu(2, &halted)) + cpu_relax(); + assert(psci_affinity_info(2) == PSCI_0_2_AFFINITY_LEVEL_ON); + + mdelay(500); + do_migration(); + mdelay(500); +} + +static void migrate_check(void) +{ + report("CPU0 runnable", psci_affinity_info(0) == PSCI_0_2_AFFINITY_LEVEL_ON); + report("CPU1 stopped", psci_affinity_info(1) == PSCI_0_2_AFFINITY_LEVEL_OFF); + report("CPU2 not stopped", psci_affinity_info(2) == PSCI_0_2_AFFINITY_LEVEL_ON); + report("CPU2 still halted", cpumask_test_cpu(2, &halted)); +} + +static struct test { + const char *name; + int nr_cpus; + void (*prepare)(void); + void (*check)(void); + void (*exit)(void); +} tests[] = { +{ + .name = "general", + .nr_cpus = 2, + .check = general_check, + .exit = general_exit, +}, +{ + .name = "migrate", + .nr_cpus = 3, + .prepare = migrate_prepare, + .check = migrate_check, +}, + {}, +}; + +static struct test *get_test(const char *name) +{ + struct test *test = &tests[0]; + while (test->name && strcmp(test->name, name)) + ++test; + return test->name ? test : NULL; +} + +int main(int ac, char **av) +{ + int psci_version = psci_invoke(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); + struct test *test = get_test("general"); + + report_info("PSCI version %d.%d", PSCI_VERSION_MAJOR(psci_version), + PSCI_VERSION_MINOR(psci_version)); + + if (ac > 1) + test = get_test(av[1]); + + if (nr_cpus < test->nr_cpus) { + report_skip("At least %d cpus required", test->nr_cpus); + goto done; + } + + if (test->prepare) + test->prepare(); + if (test->check) + test->check(); + if (test->exit) + test->exit(); + +done: + return report_summary(); +} diff --git a/arm/unittests.cfg b/arm/unittests.cfg index 44b98cfc7afd..cf5a3f5c51d2 100644 --- a/arm/unittests.cfg +++ b/arm/unittests.cfg @@ -110,6 +110,13 @@ file = psci.flat smp = $MAX_SMP groups = psci +[psci-migration] +file = psci.flat +smp = $MAX_SMP +extra_params = -append 'migrate' +timeout = 15s +groups = psci,migration + # Timer tests [timer] file = timer.flat -- 2.13.6