xref: /kvm-unit-tests/arm/cache.c (revision 3c13c64209599d16c1b04655144af5097aabb857)
12861c740SAlexandru Elisei #include <libcflat.h>
22861c740SAlexandru Elisei #include <alloc_page.h>
32861c740SAlexandru Elisei #include <asm/mmu.h>
42861c740SAlexandru Elisei #include <asm/processor.h>
5*02f1cdc8SAlexandru Elisei #include <asm/thread_info.h>
62861c740SAlexandru Elisei 
72861c740SAlexandru Elisei #define NTIMES			(1 << 16)
82861c740SAlexandru Elisei 
92861c740SAlexandru Elisei #define CTR_DIC			(1UL << 29)
102861c740SAlexandru Elisei #define CTR_IDC			(1UL << 28)
112861c740SAlexandru Elisei 
122861c740SAlexandru Elisei #define CLIDR_LOC_SHIFT		24
132861c740SAlexandru Elisei #define CLIDR_LOC_MASK		(7UL << CLIDR_LOC_SHIFT)
142861c740SAlexandru Elisei #define CLIDR_LOUU_SHIFT	27
152861c740SAlexandru Elisei #define CLIDR_LOUU_MASK		(7UL << CLIDR_LOUU_SHIFT)
162861c740SAlexandru Elisei #define CLIDR_LOUIS_SHIFT	21
172861c740SAlexandru Elisei #define CLIDR_LOUIS_MASK	(7UL << CLIDR_LOUIS_SHIFT)
182861c740SAlexandru Elisei 
192861c740SAlexandru Elisei #define RET			0xd65f03c0
202861c740SAlexandru Elisei #define MOV_X0(x)		(0xd2800000 | (((x) & 0xffff) << 5))
212861c740SAlexandru Elisei 
222861c740SAlexandru Elisei #define clean_dcache_pou(addr)			\
232861c740SAlexandru Elisei 	asm volatile("dc cvau, %0\n" :: "r" (addr) : "memory")
242861c740SAlexandru Elisei #define inval_icache_pou(addr)			\
252861c740SAlexandru Elisei 	asm volatile("ic ivau, %0\n" :: "r" (addr) : "memory")
262861c740SAlexandru Elisei 
272861c740SAlexandru Elisei typedef int (*fn_t)(void);
282861c740SAlexandru Elisei 
prime_icache(u32 * code,u32 insn)292861c740SAlexandru Elisei static inline void prime_icache(u32 *code, u32 insn)
302861c740SAlexandru Elisei {
312861c740SAlexandru Elisei 	*code = insn;
322861c740SAlexandru Elisei 	/* This is the sequence recommended in ARM DDI 0487E.a, page B2-136. */
332861c740SAlexandru Elisei 	clean_dcache_pou(code);
342861c740SAlexandru Elisei 	dsb(ish);
352861c740SAlexandru Elisei 	inval_icache_pou(code);
362861c740SAlexandru Elisei 	dsb(ish);
372861c740SAlexandru Elisei 	isb();
382861c740SAlexandru Elisei 
392861c740SAlexandru Elisei 	((fn_t)code)();
402861c740SAlexandru Elisei }
412861c740SAlexandru Elisei 
check_code_generation(bool dcache_clean,bool icache_inval)422861c740SAlexandru Elisei static void check_code_generation(bool dcache_clean, bool icache_inval)
432861c740SAlexandru Elisei {
442861c740SAlexandru Elisei 	u32 fn[] = {MOV_X0(0x42), RET};
452861c740SAlexandru Elisei 	u32 *code = alloc_page();
462861c740SAlexandru Elisei 	unsigned long sctlr;
472861c740SAlexandru Elisei 	int i, ret;
482861c740SAlexandru Elisei 	bool success;
492861c740SAlexandru Elisei 
502861c740SAlexandru Elisei 	/* Make sure we can execute from a writable page */
51*02f1cdc8SAlexandru Elisei 	mmu_clear_user(current_thread_info()->pgtable, (unsigned long)code);
522861c740SAlexandru Elisei 
532861c740SAlexandru Elisei 	sctlr = read_sysreg(sctlr_el1);
542861c740SAlexandru Elisei 	if (sctlr & SCTLR_EL1_WXN) {
552861c740SAlexandru Elisei 		sctlr &= ~SCTLR_EL1_WXN;
562861c740SAlexandru Elisei 		write_sysreg(sctlr, sctlr_el1);
572861c740SAlexandru Elisei 		isb();
582861c740SAlexandru Elisei 		/* SCTLR_EL1.WXN is permitted to be cached in a TLB. */
592861c740SAlexandru Elisei 		flush_tlb_all();
602861c740SAlexandru Elisei 	}
612861c740SAlexandru Elisei 
622861c740SAlexandru Elisei 	for (i = 0; i < ARRAY_SIZE(fn); i++) {
632861c740SAlexandru Elisei 		*(code + i) = fn[i];
642861c740SAlexandru Elisei 		clean_dcache_pou(code + i);
652861c740SAlexandru Elisei 		dsb(ish);
662861c740SAlexandru Elisei 		inval_icache_pou(code + i);
672861c740SAlexandru Elisei 	}
682861c740SAlexandru Elisei 	dsb(ish);
692861c740SAlexandru Elisei 	isb();
702861c740SAlexandru Elisei 
712861c740SAlexandru Elisei 	/* Sanity check */
722861c740SAlexandru Elisei 	((fn_t)code)();
732861c740SAlexandru Elisei 
742861c740SAlexandru Elisei 	success = true;
752861c740SAlexandru Elisei 	for (i = 0; i < NTIMES; i++) {
762861c740SAlexandru Elisei 		prime_icache(code, MOV_X0(0x42));
772861c740SAlexandru Elisei 		*code = MOV_X0(0x66);
782861c740SAlexandru Elisei 		if (dcache_clean)
792861c740SAlexandru Elisei 			clean_dcache_pou(code);
802861c740SAlexandru Elisei 		if (icache_inval) {
812861c740SAlexandru Elisei 			if (dcache_clean)
822861c740SAlexandru Elisei 				dsb(ish);
832861c740SAlexandru Elisei 			inval_icache_pou(code);
842861c740SAlexandru Elisei 		}
852861c740SAlexandru Elisei 		dsb(ish);
862861c740SAlexandru Elisei 		isb();
872861c740SAlexandru Elisei 
882861c740SAlexandru Elisei 		ret = ((fn_t)code)();
892861c740SAlexandru Elisei 		success &= (ret == 0x66);
902861c740SAlexandru Elisei 	}
912861c740SAlexandru Elisei 
92a299895bSThomas Huth 	report(success, "code generation");
932861c740SAlexandru Elisei }
942861c740SAlexandru Elisei 
main(int argc,char ** argv)952861c740SAlexandru Elisei int main(int argc, char **argv)
962861c740SAlexandru Elisei {
972861c740SAlexandru Elisei 	u64 ctr, clidr;
982861c740SAlexandru Elisei 	bool dcache_clean, icache_inval;
992861c740SAlexandru Elisei 
1002861c740SAlexandru Elisei 	report_prefix_push("IDC-DIC");
1012861c740SAlexandru Elisei 
1022861c740SAlexandru Elisei 	ctr = read_sysreg(ctr_el0);
1032861c740SAlexandru Elisei 	dcache_clean = !(ctr & CTR_IDC);
1042861c740SAlexandru Elisei 	icache_inval = !(ctr & CTR_DIC);
1052861c740SAlexandru Elisei 
1062861c740SAlexandru Elisei 	if (dcache_clean) {
1072861c740SAlexandru Elisei 		clidr = read_sysreg(clidr_el1);
1082861c740SAlexandru Elisei 		if ((clidr & CLIDR_LOC_MASK) == 0)
1092861c740SAlexandru Elisei 			dcache_clean = false;
1102861c740SAlexandru Elisei 		if ((clidr & CLIDR_LOUU_MASK) == 0 &&
1112861c740SAlexandru Elisei 		    (clidr & CLIDR_LOUIS_MASK) == 0)
1122861c740SAlexandru Elisei 			dcache_clean = false;
1132861c740SAlexandru Elisei 	}
1142861c740SAlexandru Elisei 
1152861c740SAlexandru Elisei 	if (dcache_clean)
1162861c740SAlexandru Elisei 		report_info("dcache clean to PoU required");
1172861c740SAlexandru Elisei 	if (icache_inval)
1182861c740SAlexandru Elisei 		report_info("icache invalidation to PoU required");
1192861c740SAlexandru Elisei 
1202861c740SAlexandru Elisei 	check_code_generation(dcache_clean, icache_inval);
1212861c740SAlexandru Elisei 
1222861c740SAlexandru Elisei 	return report_summary();
1232861c740SAlexandru Elisei }
124