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