#include #include #include #include #include #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(current_thread_info()->pgtable, (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(success, "code generation"); } 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(); }