@@ -25,6 +25,7 @@ OBJDIRS += lib/arm64
# arm64 specific tests
tests = $(TEST_DIR)/timer.flat
tests += $(TEST_DIR)/micro-bench.flat
+tests += $(TEST_DIR)/cache.flat
include $(SRCDIR)/$(TEST_DIR)/Makefile.common
new file mode 100644
@@ -0,0 +1,122 @@
+#include <libcflat.h>
+#include <alloc_page.h>
+#include <asm/mmu.h>
+#include <asm/processor.h>
+
+#define NTIMES (1 << 16)
+
+#define CTR_DIC (1UL << 29)
+#define CTR_IDC (1UL << 28)
+
+#define CLIDR_LOC_SHIFT 24
+#define CLIDR_LOC_MASK (7UL << CLIDR_LOC_SHIFT)
+#define CLIDR_LOUU_SHIFT 27
+#define CLIDR_LOUU_MASK (7UL << CLIDR_LOUU_SHIFT)
+#define CLIDR_LOUIS_SHIFT 21
+#define CLIDR_LOUIS_MASK (7UL << CLIDR_LOUIS_SHIFT)
+
+#define RET 0xd65f03c0
+#define MOV_X0(x) (0xd2800000 | (((x) & 0xffff) << 5))
+
+#define clean_dcache_pou(addr) \
+ asm volatile("dc cvau, %0\n" :: "r" (addr) : "memory")
+#define inval_icache_pou(addr) \
+ asm volatile("ic ivau, %0\n" :: "r" (addr) : "memory")
+
+typedef int (*fn_t)(void);
+
+static inline void prime_icache(u32 *code, u32 insn)
+{
+ *code = insn;
+ /* This is the sequence recommended in ARM DDI 0487E.a, page B2-136. */
+ clean_dcache_pou(code);
+ dsb(ish);
+ inval_icache_pou(code);
+ dsb(ish);
+ isb();
+
+ ((fn_t)code)();
+}
+
+static void check_code_generation(bool dcache_clean, bool icache_inval)
+{
+ u32 fn[] = {MOV_X0(0x42), RET};
+ u32 *code = alloc_page();
+ unsigned long sctlr;
+ int i, ret;
+ bool success;
+
+ /* Make sure we can execute from a writable page */
+ mmu_clear_user((unsigned long)code);
+
+ sctlr = read_sysreg(sctlr_el1);
+ if (sctlr & SCTLR_EL1_WXN) {
+ sctlr &= ~SCTLR_EL1_WXN;
+ write_sysreg(sctlr, sctlr_el1);
+ isb();
+ /* SCTLR_EL1.WXN is permitted to be cached in a TLB. */
+ flush_tlb_all();
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fn); i++) {
+ *(code + i) = fn[i];
+ clean_dcache_pou(code + i);
+ dsb(ish);
+ inval_icache_pou(code + i);
+ }
+ dsb(ish);
+ isb();
+
+ /* Sanity check */
+ ((fn_t)code)();
+
+ success = true;
+ for (i = 0; i < NTIMES; i++) {
+ prime_icache(code, MOV_X0(0x42));
+ *code = MOV_X0(0x66);
+ if (dcache_clean)
+ clean_dcache_pou(code);
+ if (icache_inval) {
+ if (dcache_clean)
+ dsb(ish);
+ inval_icache_pou(code);
+ }
+ dsb(ish);
+ isb();
+
+ ret = ((fn_t)code)();
+ success &= (ret == 0x66);
+ }
+
+ report("code generation", success);
+}
+
+int main(int argc, char **argv)
+{
+ u64 ctr, clidr;
+ bool dcache_clean, icache_inval;
+
+ report_prefix_push("IDC-DIC");
+
+ ctr = read_sysreg(ctr_el0);
+ dcache_clean = !(ctr & CTR_IDC);
+ icache_inval = !(ctr & CTR_DIC);
+
+ if (dcache_clean) {
+ clidr = read_sysreg(clidr_el1);
+ if ((clidr & CLIDR_LOC_MASK) == 0)
+ dcache_clean = false;
+ if ((clidr & CLIDR_LOUU_MASK) == 0 &&
+ (clidr & CLIDR_LOUIS_MASK) == 0)
+ dcache_clean = false;
+ }
+
+ if (dcache_clean)
+ report_info("dcache clean to PoU required");
+ if (icache_inval)
+ report_info("icache invalidation to PoU required");
+
+ check_code_generation(dcache_clean, icache_inval);
+
+ return report_summary();
+}
@@ -142,3 +142,9 @@ smp = 2
groups = nodefault,micro-bench
accel = kvm
arch = arm64
+
+# Cache emulation tests
+[cache]
+file = cache.flat
+arch = arm64
+groups = cache