| #include <libcflat.h> |
| #include <alloc_page.h> |
| #include <asm/mmu.h> |
| #include <asm/processor.h> |
| #include <asm/thread_info.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(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(); |
| } |