xref: /kvm-unit-tests/arm/cache.c (revision 3c13c64209599d16c1b04655144af5097aabb857)
1 #include <libcflat.h>
2 #include <alloc_page.h>
3 #include <asm/mmu.h>
4 #include <asm/processor.h>
5 #include <asm/thread_info.h>
6 
7 #define NTIMES			(1 << 16)
8 
9 #define CTR_DIC			(1UL << 29)
10 #define CTR_IDC			(1UL << 28)
11 
12 #define CLIDR_LOC_SHIFT		24
13 #define CLIDR_LOC_MASK		(7UL << CLIDR_LOC_SHIFT)
14 #define CLIDR_LOUU_SHIFT	27
15 #define CLIDR_LOUU_MASK		(7UL << CLIDR_LOUU_SHIFT)
16 #define CLIDR_LOUIS_SHIFT	21
17 #define CLIDR_LOUIS_MASK	(7UL << CLIDR_LOUIS_SHIFT)
18 
19 #define RET			0xd65f03c0
20 #define MOV_X0(x)		(0xd2800000 | (((x) & 0xffff) << 5))
21 
22 #define clean_dcache_pou(addr)			\
23 	asm volatile("dc cvau, %0\n" :: "r" (addr) : "memory")
24 #define inval_icache_pou(addr)			\
25 	asm volatile("ic ivau, %0\n" :: "r" (addr) : "memory")
26 
27 typedef int (*fn_t)(void);
28 
prime_icache(u32 * code,u32 insn)29 static inline void prime_icache(u32 *code, u32 insn)
30 {
31 	*code = insn;
32 	/* This is the sequence recommended in ARM DDI 0487E.a, page B2-136. */
33 	clean_dcache_pou(code);
34 	dsb(ish);
35 	inval_icache_pou(code);
36 	dsb(ish);
37 	isb();
38 
39 	((fn_t)code)();
40 }
41 
check_code_generation(bool dcache_clean,bool icache_inval)42 static void check_code_generation(bool dcache_clean, bool icache_inval)
43 {
44 	u32 fn[] = {MOV_X0(0x42), RET};
45 	u32 *code = alloc_page();
46 	unsigned long sctlr;
47 	int i, ret;
48 	bool success;
49 
50 	/* Make sure we can execute from a writable page */
51 	mmu_clear_user(current_thread_info()->pgtable, (unsigned long)code);
52 
53 	sctlr = read_sysreg(sctlr_el1);
54 	if (sctlr & SCTLR_EL1_WXN) {
55 		sctlr &= ~SCTLR_EL1_WXN;
56 		write_sysreg(sctlr, sctlr_el1);
57 		isb();
58 		/* SCTLR_EL1.WXN is permitted to be cached in a TLB. */
59 		flush_tlb_all();
60 	}
61 
62 	for (i = 0; i < ARRAY_SIZE(fn); i++) {
63 		*(code + i) = fn[i];
64 		clean_dcache_pou(code + i);
65 		dsb(ish);
66 		inval_icache_pou(code + i);
67 	}
68 	dsb(ish);
69 	isb();
70 
71 	/* Sanity check */
72 	((fn_t)code)();
73 
74 	success = true;
75 	for (i = 0; i < NTIMES; i++) {
76 		prime_icache(code, MOV_X0(0x42));
77 		*code = MOV_X0(0x66);
78 		if (dcache_clean)
79 			clean_dcache_pou(code);
80 		if (icache_inval) {
81 			if (dcache_clean)
82 				dsb(ish);
83 			inval_icache_pou(code);
84 		}
85 		dsb(ish);
86 		isb();
87 
88 		ret = ((fn_t)code)();
89 		success &= (ret == 0x66);
90 	}
91 
92 	report(success, "code generation");
93 }
94 
main(int argc,char ** argv)95 int main(int argc, char **argv)
96 {
97 	u64 ctr, clidr;
98 	bool dcache_clean, icache_inval;
99 
100 	report_prefix_push("IDC-DIC");
101 
102 	ctr = read_sysreg(ctr_el0);
103 	dcache_clean = !(ctr & CTR_IDC);
104 	icache_inval = !(ctr & CTR_DIC);
105 
106 	if (dcache_clean) {
107 		clidr = read_sysreg(clidr_el1);
108 		if ((clidr & CLIDR_LOC_MASK) == 0)
109 			dcache_clean = false;
110 		if ((clidr & CLIDR_LOUU_MASK) == 0 &&
111 		    (clidr & CLIDR_LOUIS_MASK) == 0)
112 			dcache_clean = false;
113 	}
114 
115 	if (dcache_clean)
116 		report_info("dcache clean to PoU required");
117 	if (icache_inval)
118 		report_info("icache invalidation to PoU required");
119 
120 	check_code_generation(dcache_clean, icache_inval);
121 
122 	return report_summary();
123 }
124