blob: 2756066fd4e92ebaacf7afe2f2083bbe45010734 [file] [log] [blame] [edit]
#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();
}