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