Basic torture test that continuously modifies a single instruction opcode and checks if all modifications were done and run as expected. Signed-off-by: Alexander Spyridakis <a.spyridakis@xxxxxxxxxxxxxxxxxxxxxx> --- arm/self-modifying-test.c | 109 +++++++++++++++++++++++++++++++++++++++++++ config/config-arm-common.mak | 2 + 2 files changed, 111 insertions(+) create mode 100644 arm/self-modifying-test.c diff --git a/arm/self-modifying-test.c b/arm/self-modifying-test.c new file mode 100644 index 0000000..cab47a0 --- /dev/null +++ b/arm/self-modifying-test.c @@ -0,0 +1,109 @@ +/* + * Basic self-modifying code test case + * + * Copyright (C) 2015 Virtual Open Systems SAS + * Author: Alexander Spyridakis <a.spyridakis@xxxxxxxxxxxxxxxxxxxxxx> + * + * This work is licensed under the terms of the GNU LGPL, version 2. + */ + +#include <asm/smp.h> +#include <asm/mmu.h> +#include <asm/spinlock.h> + +#define LOOP_SIZE 100000 + +int result; +static cpumask_t smp_test_complete; +static struct spinlock lock; + +void self_modifying_test(void) +{ + extern void *smc; + static int toggle; + int i, cpu = smp_processor_id(), *ptr = (int *)&smc; + + printf("CPU%d starting test\n", cpu); + + for (i = 0; i < LOOP_SIZE; i++) { + /* Don't run concurrently with other CPUs*/ + spin_lock(&lock); + + /* A simple snippet that increments a memory value which + will be modified immediately after. Before running, + invalidate the instruction and data cache for that + specific virtual address */ +#ifdef __arm__ + asm("mcr p15, 0, %0, c7, c6, 1\n" /* DCIMVAC */ + "mcr p15, 0, %0, c7, c5, 1\n" /* ICIMVAU */ +#else + asm("dc ivac, %0\n" + "ic ivau, %0\n" +#endif + "dsb ish\n" + "isb\n" + "smc:\n" + "add %1, %1, #1\n" + "str %1, [%2]\n" + :: "r" (ptr), "r" (result), "r" (&result)); + + /* Overwrite the previous labelled opcode, + toggle between incrementing by one or two */ + toggle ^= 1; + if (toggle) +#ifdef __arm__ + *ptr += 1; + else + *ptr -= 1; +#else + { + *ptr &= ~(1 << 10); + *ptr |= (1 << 11); + } else { + *ptr |= (1 << 10); + *ptr &= ~(1 << 11); + } +#endif + + spin_unlock(&lock); + } + + cpumask_set_cpu(cpu, &smp_test_complete); + if (cpu != 0) + halt(); +} + +int main(int argc, char **argv) +{ + int cpu, calc; + (void)argc, (void)argv; + + /* Set memory as writeable, on ARMv7 we need to re-enable the MMU */ +#ifdef __arm__ + mmu_disable(); + flush_tlb_all(); + mmu_set_range_ptes(mmu_idmap, PHYS_OFFSET, PHYS_OFFSET, PHYS_END, + __pgprot(PTE_WBWA)); + mmu_enable(mmu_idmap); +#else + mmu_set_range_ptes(mmu_idmap, PHYS_OFFSET, PHYS_OFFSET, PHYS_END, + __pgprot(PTE_WBWA)); + flush_tlb_all(); +#endif + + for_each_present_cpu(cpu) { + if (cpu == 0) + continue; + smp_boot_secondary(cpu, self_modifying_test); + } + + self_modifying_test(); + + while (!cpumask_full(&smp_test_complete)) + cpu_relax(); + + calc = LOOP_SIZE * nr_cpus + (LOOP_SIZE * nr_cpus / 2); + report("Result: %d - Expected: %d\n", result == calc, result, calc); + + return report_summary(); +} diff --git a/config/config-arm-common.mak b/config/config-arm-common.mak index 698555d..74a73e4 100644 --- a/config/config-arm-common.mak +++ b/config/config-arm-common.mak @@ -10,6 +10,7 @@ ifeq ($(LOADADDR),) endif tests-common = \ + $(TEST_DIR)/self-modifying-test.flat \ $(TEST_DIR)/selftest.flat \ $(TEST_DIR)/spinlock-test.flat @@ -70,3 +71,4 @@ test_cases: $(generated_files) $(tests-common) $(tests) $(TEST_DIR)/selftest.elf: $(cstart.o) $(TEST_DIR)/selftest.o $(TEST_DIR)/spinlock-test.elf: $(cstart.o) $(TEST_DIR)/spinlock-test.o +$(TEST_DIR)/self-modifying-test.elf: $(cstart.o) $(TEST_DIR)/self-modifying-test.o -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html