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