xref: /linux/drivers/misc/lkdtm/powerpc.c (revision 122b52f0bab007ebeb414c8280c1def17b9ed1f4)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include "lkdtm.h"
4 #include <linux/slab.h>
5 #include <linux/vmalloc.h>
6 #include <asm/mmu.h>
7 
8 #ifdef CONFIG_PPC_64S_HASH_MMU
9 /* Inserts new slb entries */
10 static void insert_slb_entry(unsigned long p, int ssize, int page_size)
11 {
12 	unsigned long flags;
13 
14 	flags = SLB_VSID_KERNEL | mmu_psize_defs[page_size].sllp;
15 	preempt_disable();
16 
17 	asm volatile("slbmte %0,%1" :
18 		     : "r" (mk_vsid_data(p, ssize, flags)),
19 		       "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED))
20 		     : "memory");
21 	isync();
22 
23 	asm volatile("slbmte %0,%1" :
24 			: "r" (mk_vsid_data(p, ssize, flags)),
25 			  "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED + 1))
26 			: "memory");
27 	isync();
28 
29 	preempt_enable();
30 }
31 
32 /* Inject slb multihit on vmalloc-ed address i.e 0xD00... */
33 static int inject_vmalloc_slb_multihit(void)
34 {
35 	char *p;
36 
37 	p = vmalloc(PAGE_SIZE);
38 	if (!p)
39 		return -ENOMEM;
40 
41 	insert_slb_entry((unsigned long)p, MMU_SEGSIZE_1T, mmu_vmalloc_psize);
42 	/*
43 	 * This triggers exception, If handled correctly we must recover
44 	 * from this error.
45 	 */
46 	p[0] = '!';
47 	vfree(p);
48 	return 0;
49 }
50 
51 /* Inject slb multihit on kmalloc-ed address i.e 0xC00... */
52 static int inject_kmalloc_slb_multihit(void)
53 {
54 	char *p;
55 
56 	p = kmalloc(2048, GFP_KERNEL);
57 	if (!p)
58 		return -ENOMEM;
59 
60 	insert_slb_entry((unsigned long)p, MMU_SEGSIZE_1T, mmu_linear_psize);
61 	/*
62 	 * This triggers exception, If handled correctly we must recover
63 	 * from this error.
64 	 */
65 	p[0] = '!';
66 	kfree(p);
67 	return 0;
68 }
69 
70 /*
71  * Few initial SLB entries are bolted. Add a test to inject
72  * multihit in bolted entry 0.
73  */
74 static void insert_dup_slb_entry_0(void)
75 {
76 	unsigned long test_address = PAGE_OFFSET, *test_ptr;
77 	unsigned long esid, vsid;
78 	unsigned long i = 0;
79 
80 	test_ptr = (unsigned long *)test_address;
81 	preempt_disable();
82 
83 	asm volatile("slbmfee  %0,%1" : "=r" (esid) : "r" (i));
84 	asm volatile("slbmfev  %0,%1" : "=r" (vsid) : "r" (i));
85 
86 	/* for i !=0 we would need to mask out the old entry number */
87 	asm volatile("slbmte %0,%1" :
88 			: "r" (vsid),
89 			  "r" (esid | SLB_NUM_BOLTED)
90 			: "memory");
91 	isync();
92 
93 	asm volatile("slbmfee  %0,%1" : "=r" (esid) : "r" (i));
94 	asm volatile("slbmfev  %0,%1" : "=r" (vsid) : "r" (i));
95 
96 	/* for i !=0 we would need to mask out the old entry number */
97 	asm volatile("slbmte %0,%1" :
98 			: "r" (vsid),
99 			  "r" (esid | (SLB_NUM_BOLTED + 1))
100 			: "memory");
101 	isync();
102 
103 	pr_info("%s accessing test address 0x%lx: 0x%lx\n",
104 		__func__, test_address, *test_ptr);
105 
106 	preempt_enable();
107 }
108 #endif /* CONFIG_PPC_64S_HASH_MMU */
109 
110 static __always_inline void tlbiel_va(unsigned long va,
111 				      unsigned long pid,
112 				      unsigned long ap,
113 				      unsigned long ric)
114 {
115 	unsigned long rb, rs, prs, r;
116 
117 	rb = va & ~(PPC_BITMASK(52, 63));
118 	rb |= ap << PPC_BITLSHIFT(58);
119 	rs = pid << PPC_BITLSHIFT(31);
120 
121 	prs = 1; /* process scoped */
122 	r = 1;   /* radix format */
123 
124 	/*
125 	 * Trigger an MCE by issuing radix tlbiel with an invalid operand combination.
126 	 * The combination of RIC = 2 with IS = 0 (Invalidation selector specified
127 	 * in the RB register) is invalid.
128 	 * This invalid combination causes hardware to raise a machine check.
129 	 */
130 	asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1)
131 			: : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
132 }
133 
134 static void lkdtm_PPC_SLB_MULTIHIT(void)
135 {
136 #ifdef CONFIG_PPC_64S_HASH_MMU
137 	if (!radix_enabled()) {
138 		pr_info("Injecting SLB multihit errors\n");
139 		/*
140 		 * These need not be separate tests, And they do pretty
141 		 * much same thing. In any case we must recover from the
142 		 * errors introduced by these functions, machine would not
143 		 * survive these tests in case of failure to handle.
144 		 */
145 		inject_vmalloc_slb_multihit();
146 		inject_kmalloc_slb_multihit();
147 		insert_dup_slb_entry_0();
148 		pr_info("Recovered from SLB multihit errors\n");
149 	} else {
150 		pr_err("XFAIL: This test is for ppc64 and with hash mode MMU only\n");
151 	}
152 #else
153 	pr_err("XFAIL: This test requires CONFIG_PPC_64S_HASH_MMU\n");
154 #endif
155 }
156 
157 static void lkdtm_PPC_RADIX_TLBIEL(void)
158 {
159 	unsigned long addr = PAGE_OFFSET;
160 
161 	if (radix_enabled()) {
162 		pr_info("Injecting Radix TLB invalidation MCE\n");
163 		tlbiel_va(addr, 0, 0, RIC_FLUSH_ALL);
164 		pr_info("Recovered from radix tlbiel attempt\n");
165 	} else {
166 		pr_err("XFAIL: This test is for ppc64 and with radix mode MMU only\n");
167 	}
168 }
169 
170 static struct crashtype crashtypes[] = {
171 	CRASHTYPE(PPC_SLB_MULTIHIT),
172 	CRASHTYPE(PPC_RADIX_TLBIEL),
173 };
174 
175 struct crashtype_category powerpc_crashtypes = {
176 	.crashtypes = crashtypes,
177 	.len	    = ARRAY_SIZE(crashtypes),
178 };
179