new file mode 100644
@@ -0,0 +1,109 @@
+/*
+ * Basic self-modifying code test case
+ *
+ * Copyright (C) 2015 Virtual Open Systems SAS
+ * Author: Alexander Spyridakis <a.spyridakis@virtualopensystems.com>
+ *
+ * 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();
+}
@@ -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
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@virtualopensystems.com> --- 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