xref: /kvm-unit-tests/arm/mte.c (revision abdc5d02a7796a55802509ac9bb704c721f2a5f6)
1*1b59c632SVladimir Murzin /* SPDX-License-Identifier: GPL-2.0 */
2*1b59c632SVladimir Murzin /*
3*1b59c632SVladimir Murzin  * Copyright (C) 2024 Arm Limited.
4*1b59c632SVladimir Murzin  * All rights reserved.
5*1b59c632SVladimir Murzin  */
6*1b59c632SVladimir Murzin 
7*1b59c632SVladimir Murzin #include <libcflat.h>
8*1b59c632SVladimir Murzin #include <alloc_page.h>
9*1b59c632SVladimir Murzin #include <stdlib.h>
10*1b59c632SVladimir Murzin 
11*1b59c632SVladimir Murzin #include <asm/mmu.h>
12*1b59c632SVladimir Murzin #include <asm/pgtable-hwdef.h>
13*1b59c632SVladimir Murzin #include <asm/processor.h>
14*1b59c632SVladimir Murzin #include <asm/sysreg.h>
15*1b59c632SVladimir Murzin #include <asm/thread_info.h>
16*1b59c632SVladimir Murzin 
17*1b59c632SVladimir Murzin 
18*1b59c632SVladimir Murzin /* Tag Check Faults cause a synchronous exception */
19*1b59c632SVladimir Murzin #define MTE_TCF_SYNC	0b01
20*1b59c632SVladimir Murzin /* Tag Check Faults are asynchronously accumulated */
21*1b59c632SVladimir Murzin #define MTE_TCF_ASYNC	0b10
22*1b59c632SVladimir Murzin /*
23*1b59c632SVladimir Murzin  * Tag Check Faults cause a synchronous exception on reads,
24*1b59c632SVladimir Murzin  * and are asynchronously accumulated on writes
25*1b59c632SVladimir Murzin  */
26*1b59c632SVladimir Murzin #define MTE_TCF_ASYMM	0b11
27*1b59c632SVladimir Murzin 
28*1b59c632SVladimir Murzin #define MTE_GRANULE_SIZE        UL(16)
29*1b59c632SVladimir Murzin #define MTE_GRANULE_MASK        (~(MTE_GRANULE_SIZE - 1))
30*1b59c632SVladimir Murzin #define MTE_TAG_SHIFT           56
31*1b59c632SVladimir Murzin 
32*1b59c632SVladimir Murzin #define untagged(p)									\
33*1b59c632SVladimir Murzin ({											\
34*1b59c632SVladimir Murzin 	unsigned long __in = (unsigned long)(p);					\
35*1b59c632SVladimir Murzin 	typeof(p) __out = (typeof(p))(__in & ~(MTE_GRANULE_MASK << MTE_TAG_SHIFT));	\
36*1b59c632SVladimir Murzin 											\
37*1b59c632SVladimir Murzin 	__out;										\
38*1b59c632SVladimir Murzin })
39*1b59c632SVladimir Murzin 
40*1b59c632SVladimir Murzin #define tagged(p, t)							\
41*1b59c632SVladimir Murzin ({									\
42*1b59c632SVladimir Murzin 	unsigned long __in = (unsigned long)(untagged(p));		\
43*1b59c632SVladimir Murzin 	unsigned long __tag = (unsigned long)(t) << MTE_TAG_SHIFT;	\
44*1b59c632SVladimir Murzin 	typeof(p) __out = (typeof(p))(__in | __tag);			\
45*1b59c632SVladimir Murzin 									\
46*1b59c632SVladimir Murzin 	__out;								\
47*1b59c632SVladimir Murzin })
48*1b59c632SVladimir Murzin 
49*1b59c632SVladimir Murzin /*
50*1b59c632SVladimir Murzin  * If we use a normal (non hand coded inline assembly) load or store
51*1b59c632SVladimir Murzin  * to access a tagged address, the compiler will reasonably assume
52*1b59c632SVladimir Murzin  * that the access succeeded, and the next instruction may do
53*1b59c632SVladimir Murzin  * something based on that assumption.
54*1b59c632SVladimir Murzin  *
55*1b59c632SVladimir Murzin  * But a test might want the tagged access to fail on purpose, and if
56*1b59c632SVladimir Murzin  * we advance the PC to the next instruction, the one added by the
57*1b59c632SVladimir Murzin  * compiler, we might leave the program in an unexpected state.
58*1b59c632SVladimir Murzin  */
mem_read(unsigned int * addr,unsigned int * res)59*1b59c632SVladimir Murzin static inline void mem_read(unsigned int *addr, unsigned int *res)
60*1b59c632SVladimir Murzin {
61*1b59c632SVladimir Murzin 	unsigned int r;
62*1b59c632SVladimir Murzin 
63*1b59c632SVladimir Murzin 	asm volatile ("ldr %w0,[%1]\n"
64*1b59c632SVladimir Murzin 		      "str %w0,[%2]\n"
65*1b59c632SVladimir Murzin 		      : "=&r" (r)
66*1b59c632SVladimir Murzin 		      : "r" (addr), "r" (res) : "memory");
67*1b59c632SVladimir Murzin }
68*1b59c632SVladimir Murzin 
mem_write(unsigned int * addr,unsigned int val)69*1b59c632SVladimir Murzin static inline void mem_write(unsigned int *addr, unsigned int val)
70*1b59c632SVladimir Murzin {
71*1b59c632SVladimir Murzin 	/* The NOP allows the same exception handler as mem_read() to be used. */
72*1b59c632SVladimir Murzin 	asm volatile ("str %w0,[%1]\n"
73*1b59c632SVladimir Murzin 		      "nop\n"
74*1b59c632SVladimir Murzin 		      :
75*1b59c632SVladimir Murzin 		      : "r" (val), "r" (addr)
76*1b59c632SVladimir Murzin 		      : "memory");
77*1b59c632SVladimir Murzin }
78*1b59c632SVladimir Murzin 
79*1b59c632SVladimir Murzin static volatile bool mte_exception;
80*1b59c632SVladimir Murzin 
mte_fault_handler(struct pt_regs * regs,unsigned int esr)81*1b59c632SVladimir Murzin static void mte_fault_handler(struct pt_regs *regs, unsigned int esr)
82*1b59c632SVladimir Murzin {
83*1b59c632SVladimir Murzin 	unsigned int dfsc = esr & GENMASK(5, 0);
84*1b59c632SVladimir Murzin 	unsigned int fnv = esr & BIT(10);
85*1b59c632SVladimir Murzin 
86*1b59c632SVladimir Murzin 	if (dfsc == 0b010001) {
87*1b59c632SVladimir Murzin 		if (fnv)
88*1b59c632SVladimir Murzin 			report_info("Unexpected non-zero FnV");
89*1b59c632SVladimir Murzin 		mte_exception = true;
90*1b59c632SVladimir Murzin 	}
91*1b59c632SVladimir Murzin 
92*1b59c632SVladimir Murzin 	/*
93*1b59c632SVladimir Murzin 	 * mem_read() reads the value from the tagged pointer, then
94*1b59c632SVladimir Murzin 	 * stores this value in the untagged 'res' pointer. The
95*1b59c632SVladimir Murzin 	 * function that called mem_read() will want to check that the
96*1b59c632SVladimir Murzin 	 * initial value of 'res' hasn't changed if a tag check fault
97*1b59c632SVladimir Murzin 	 * is reported. Skip over two instructions so 'res' isn't
98*1b59c632SVladimir Murzin 	 * overwritten.
99*1b59c632SVladimir Murzin 	 */
100*1b59c632SVladimir Murzin 	regs->pc += 8;
101*1b59c632SVladimir Murzin }
102*1b59c632SVladimir Murzin 
mmu_set_tagged(pgd_t * pgtable,unsigned long vaddr)103*1b59c632SVladimir Murzin static inline void mmu_set_tagged(pgd_t *pgtable, unsigned long vaddr)
104*1b59c632SVladimir Murzin {
105*1b59c632SVladimir Murzin 	pteval_t *p_pte = follow_pte(pgtable, untagged(vaddr));
106*1b59c632SVladimir Murzin 
107*1b59c632SVladimir Murzin 	if (p_pte) {
108*1b59c632SVladimir Murzin 		pteval_t entry = *p_pte;
109*1b59c632SVladimir Murzin 
110*1b59c632SVladimir Murzin 		entry &= ~PTE_ATTRINDX_MASK;
111*1b59c632SVladimir Murzin 		entry |= PTE_ATTRINDX(MT_NORMAL_TAGGED);
112*1b59c632SVladimir Murzin 
113*1b59c632SVladimir Murzin 		WRITE_ONCE(*p_pte, entry);
114*1b59c632SVladimir Murzin 		flush_tlb_page(vaddr);
115*1b59c632SVladimir Murzin 	} else {
116*1b59c632SVladimir Murzin 		report_abort("Cannot find PTE");
117*1b59c632SVladimir Murzin 	}
118*1b59c632SVladimir Murzin }
119*1b59c632SVladimir Murzin 
mte_init(void)120*1b59c632SVladimir Murzin static void mte_init(void)
121*1b59c632SVladimir Murzin {
122*1b59c632SVladimir Murzin 	unsigned long sctlr = read_sysreg(sctlr_el1);
123*1b59c632SVladimir Murzin 	unsigned long tcr = read_sysreg(tcr_el1);
124*1b59c632SVladimir Murzin 
125*1b59c632SVladimir Murzin 	sctlr &= ~SCTLR_EL1_TCF_MASK;
126*1b59c632SVladimir Murzin 	sctlr |= SCTLR_EL1_ATA;
127*1b59c632SVladimir Murzin 
128*1b59c632SVladimir Murzin 	tcr &= ~TCR_TCMA0;
129*1b59c632SVladimir Murzin 	tcr |= TCR_TBI0;
130*1b59c632SVladimir Murzin 
131*1b59c632SVladimir Murzin 	write_sysreg(sctlr, sctlr_el1);
132*1b59c632SVladimir Murzin 	write_sysreg(tcr, tcr_el1);
133*1b59c632SVladimir Murzin 
134*1b59c632SVladimir Murzin 	isb();
135*1b59c632SVladimir Murzin 	flush_tlb_all();
136*1b59c632SVladimir Murzin }
137*1b59c632SVladimir Murzin 
mte_set_tcf(unsigned long tcf)138*1b59c632SVladimir Murzin static inline unsigned long mte_set_tcf(unsigned long tcf)
139*1b59c632SVladimir Murzin {
140*1b59c632SVladimir Murzin 	unsigned long sctlr = read_sysreg(sctlr_el1);
141*1b59c632SVladimir Murzin 	unsigned long old = (sctlr & SCTLR_EL1_TCF_MASK) >> SCTLR_EL1_TCF_SHIFT;
142*1b59c632SVladimir Murzin 
143*1b59c632SVladimir Murzin 	sctlr &= ~(SCTLR_EL1_TCF_MASK | SCTLR_EL1_TCF0_MASK);
144*1b59c632SVladimir Murzin 	sctlr |= (tcf << SCTLR_EL1_TCF_SHIFT) & SCTLR_EL1_TCF_MASK;
145*1b59c632SVladimir Murzin 
146*1b59c632SVladimir Murzin 	write_sysreg(sctlr, sctlr_el1);
147*1b59c632SVladimir Murzin 	write_sysreg_s(0, TFSR_EL1);
148*1b59c632SVladimir Murzin 	isb();
149*1b59c632SVladimir Murzin 
150*1b59c632SVladimir Murzin 	return old;
151*1b59c632SVladimir Murzin }
152*1b59c632SVladimir Murzin 
mte_set_tag(void * addr,size_t size,unsigned int tag)153*1b59c632SVladimir Murzin static inline void mte_set_tag(void *addr, size_t size, unsigned int tag)
154*1b59c632SVladimir Murzin {
155*1b59c632SVladimir Murzin #ifdef CC_HAS_MTE
156*1b59c632SVladimir Murzin 	unsigned long in = (unsigned long)untagged(addr);
157*1b59c632SVladimir Murzin 	unsigned long start = ALIGN_DOWN(in, 16);
158*1b59c632SVladimir Murzin 	unsigned long end = ALIGN(in + size, 16);
159*1b59c632SVladimir Murzin 
160*1b59c632SVladimir Murzin 	for (unsigned long ptr = start; ptr < end; ptr += 16) {
161*1b59c632SVladimir Murzin 		asm volatile(".arch   armv8.5-a+memtag\n"
162*1b59c632SVladimir Murzin 			     "stg %0, [%0]"
163*1b59c632SVladimir Murzin 			     :
164*1b59c632SVladimir Murzin 			     : "r"(tagged(ptr, tag))
165*1b59c632SVladimir Murzin 			     : "memory");
166*1b59c632SVladimir Murzin 	}
167*1b59c632SVladimir Murzin #endif
168*1b59c632SVladimir Murzin }
169*1b59c632SVladimir Murzin 
get_clear_tfsr(void)170*1b59c632SVladimir Murzin static inline unsigned long get_clear_tfsr(void)
171*1b59c632SVladimir Murzin {
172*1b59c632SVladimir Murzin 	unsigned long r;
173*1b59c632SVladimir Murzin 
174*1b59c632SVladimir Murzin 	dsb(nsh);
175*1b59c632SVladimir Murzin 	isb();
176*1b59c632SVladimir Murzin 
177*1b59c632SVladimir Murzin 	r = read_sysreg_s(TFSR_EL1);
178*1b59c632SVladimir Murzin 	write_sysreg_s(0, TFSR_EL1);
179*1b59c632SVladimir Murzin 
180*1b59c632SVladimir Murzin 	return r;
181*1b59c632SVladimir Murzin }
182*1b59c632SVladimir Murzin 
mte_sync_test(void)183*1b59c632SVladimir Murzin static void mte_sync_test(void)
184*1b59c632SVladimir Murzin {
185*1b59c632SVladimir Murzin 	unsigned int *mem = tagged(alloc_page(), 1);
186*1b59c632SVladimir Murzin 	unsigned int val = 0;
187*1b59c632SVladimir Murzin 
188*1b59c632SVladimir Murzin 	mmu_set_tagged(current_thread_info()->pgtable, (unsigned long)mem);
189*1b59c632SVladimir Murzin 	mte_set_tag(mem, PAGE_SIZE, 1);
190*1b59c632SVladimir Murzin 	memset(mem, 0xff, PAGE_SIZE);
191*1b59c632SVladimir Murzin 	mte_set_tcf(MTE_TCF_SYNC);
192*1b59c632SVladimir Murzin 
193*1b59c632SVladimir Murzin 	mte_exception = false;
194*1b59c632SVladimir Murzin 
195*1b59c632SVladimir Murzin 	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_DABT_EL1, mte_fault_handler);
196*1b59c632SVladimir Murzin 
197*1b59c632SVladimir Murzin 	mem_read(tagged(mem, 2), &val);
198*1b59c632SVladimir Murzin 
199*1b59c632SVladimir Murzin 	report((val == 0) && mte_exception && (get_clear_tfsr() == 0), "read");
200*1b59c632SVladimir Murzin 
201*1b59c632SVladimir Murzin 	mte_exception = false;
202*1b59c632SVladimir Murzin 
203*1b59c632SVladimir Murzin 	mem_write(tagged(mem, 3), 0xbbbbbbbb);
204*1b59c632SVladimir Murzin 
205*1b59c632SVladimir Murzin 	report((*mem == 0xffffffff) && mte_exception && (get_clear_tfsr() == 0), "write");
206*1b59c632SVladimir Murzin 
207*1b59c632SVladimir Murzin 	free_page(untagged(mem));
208*1b59c632SVladimir Murzin }
209*1b59c632SVladimir Murzin 
mte_asymm_test(void)210*1b59c632SVladimir Murzin static void mte_asymm_test(void)
211*1b59c632SVladimir Murzin {
212*1b59c632SVladimir Murzin 	unsigned int *mem = tagged(alloc_page(), 2);
213*1b59c632SVladimir Murzin 	unsigned int val = 0;
214*1b59c632SVladimir Murzin 
215*1b59c632SVladimir Murzin 	mmu_set_tagged(current_thread_info()->pgtable, (unsigned long)mem);
216*1b59c632SVladimir Murzin 	mte_set_tag(mem, PAGE_SIZE, 2);
217*1b59c632SVladimir Murzin 	memset(mem, 0xff, PAGE_SIZE);
218*1b59c632SVladimir Murzin 	mte_set_tcf(MTE_TCF_ASYMM);
219*1b59c632SVladimir Murzin 	mte_exception = false;
220*1b59c632SVladimir Murzin 
221*1b59c632SVladimir Murzin 	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_DABT_EL1, mte_fault_handler);
222*1b59c632SVladimir Murzin 
223*1b59c632SVladimir Murzin 	mem_read(tagged(mem, 3), &val);
224*1b59c632SVladimir Murzin 	report((val == 0) && mte_exception && (get_clear_tfsr() == 0), "read");
225*1b59c632SVladimir Murzin 
226*1b59c632SVladimir Murzin 	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_DABT_EL1, NULL);
227*1b59c632SVladimir Murzin 
228*1b59c632SVladimir Murzin 	mem_write(tagged(mem, 4), 0xaaaaaaaa);
229*1b59c632SVladimir Murzin 	report((*mem == 0xaaaaaaaa) && (get_clear_tfsr() == TFSR_EL1_TF0), "write");
230*1b59c632SVladimir Murzin 
231*1b59c632SVladimir Murzin 	free_page(untagged(mem));
232*1b59c632SVladimir Murzin }
233*1b59c632SVladimir Murzin 
mte_async_test(void)234*1b59c632SVladimir Murzin static void mte_async_test(void)
235*1b59c632SVladimir Murzin {
236*1b59c632SVladimir Murzin 	unsigned int *mem = tagged(alloc_page(), 3);
237*1b59c632SVladimir Murzin 	unsigned int val = 0;
238*1b59c632SVladimir Murzin 
239*1b59c632SVladimir Murzin 	mmu_set_tagged(current_thread_info()->pgtable, (unsigned long)mem);
240*1b59c632SVladimir Murzin 	mte_set_tag(mem, PAGE_SIZE, 3);
241*1b59c632SVladimir Murzin 	memset(mem, 0xff, PAGE_SIZE);
242*1b59c632SVladimir Murzin 	mte_set_tcf(MTE_TCF_ASYNC);
243*1b59c632SVladimir Murzin 
244*1b59c632SVladimir Murzin 	mem_read(tagged(mem, 4), &val);
245*1b59c632SVladimir Murzin 	report((val == 0xffffffff) && (get_clear_tfsr() == TFSR_EL1_TF0), "read");
246*1b59c632SVladimir Murzin 
247*1b59c632SVladimir Murzin 	mem_write(tagged(mem, 5), 0xcccccccc);
248*1b59c632SVladimir Murzin 	report((*mem == 0xcccccccc) && (get_clear_tfsr() == TFSR_EL1_TF0), "write");
249*1b59c632SVladimir Murzin 
250*1b59c632SVladimir Murzin 	free_page(untagged(mem));
251*1b59c632SVladimir Murzin }
252*1b59c632SVladimir Murzin 
mte_version(void)253*1b59c632SVladimir Murzin static unsigned int mte_version(void)
254*1b59c632SVladimir Murzin {
255*1b59c632SVladimir Murzin #ifdef CC_HAS_MTE
256*1b59c632SVladimir Murzin 	uint64_t r;
257*1b59c632SVladimir Murzin 
258*1b59c632SVladimir Murzin 	asm volatile("mrs %x0, id_aa64pfr1_el1" : "=r"(r));
259*1b59c632SVladimir Murzin 
260*1b59c632SVladimir Murzin 	return (r >> ID_AA64PFR1_EL1_MTE_SHIFT) & 0b1111;
261*1b59c632SVladimir Murzin #else
262*1b59c632SVladimir Murzin 	report_info("Compiler lack MTE support");
263*1b59c632SVladimir Murzin 	return 0;
264*1b59c632SVladimir Murzin #endif
265*1b59c632SVladimir Murzin }
266*1b59c632SVladimir Murzin 
main(int argc,char * argv[])267*1b59c632SVladimir Murzin int main(int argc, char *argv[])
268*1b59c632SVladimir Murzin {
269*1b59c632SVladimir Murzin 
270*1b59c632SVladimir Murzin 	unsigned int version = mte_version();
271*1b59c632SVladimir Murzin 
272*1b59c632SVladimir Murzin 	if (version < 2) {
273*1b59c632SVladimir Murzin 		report_skip("No MTE support, skip...\n");
274*1b59c632SVladimir Murzin 		return report_summary();
275*1b59c632SVladimir Murzin 	}
276*1b59c632SVladimir Murzin 
277*1b59c632SVladimir Murzin 	if (argc < 2)
278*1b59c632SVladimir Murzin 		report_abort("no test specified");
279*1b59c632SVladimir Murzin 
280*1b59c632SVladimir Murzin 	report_prefix_push("mte");
281*1b59c632SVladimir Murzin 
282*1b59c632SVladimir Murzin 	mte_init();
283*1b59c632SVladimir Murzin 
284*1b59c632SVladimir Murzin 	if (strcmp(argv[1], "sync") == 0) {
285*1b59c632SVladimir Murzin 		report_prefix_push(argv[1]);
286*1b59c632SVladimir Murzin 		mte_sync_test();
287*1b59c632SVladimir Murzin 		report_prefix_pop();
288*1b59c632SVladimir Murzin 	} else if (strcmp(argv[1], "async") == 0) {
289*1b59c632SVladimir Murzin 		report_prefix_push(argv[1]);
290*1b59c632SVladimir Murzin 		if (version < 3) {
291*1b59c632SVladimir Murzin 			report_skip("No MTE async, skip...\n");
292*1b59c632SVladimir Murzin 			return report_summary();
293*1b59c632SVladimir Murzin 		}
294*1b59c632SVladimir Murzin 		mte_async_test();
295*1b59c632SVladimir Murzin 		report_prefix_pop();
296*1b59c632SVladimir Murzin 	} else if (strcmp(argv[1], "asymm") == 0) {
297*1b59c632SVladimir Murzin 		report_prefix_push(argv[1]);
298*1b59c632SVladimir Murzin 		if (version < 3) {
299*1b59c632SVladimir Murzin 			report_skip("No MTE asymm, skip...\n");
300*1b59c632SVladimir Murzin 			return report_summary();
301*1b59c632SVladimir Murzin 		}
302*1b59c632SVladimir Murzin 		mte_asymm_test();
303*1b59c632SVladimir Murzin 		report_prefix_pop();
304*1b59c632SVladimir Murzin 	} else {
305*1b59c632SVladimir Murzin 		report_abort("Unknown sub-test '%s'", argv[1]);
306*1b59c632SVladimir Murzin 	}
307*1b59c632SVladimir Murzin 
308*1b59c632SVladimir Murzin 	return report_summary();
309*1b59c632SVladimir Murzin }
310