projects/linux·blob master · refs

178 lines · 4.4 KiB

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 */
10static 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... */
33static 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... */
52static 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 */
74static 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
110static __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
134static 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
157static 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
170static struct crashtype crashtypes[] = {
171 CRASHTYPE(PPC_SLB_MULTIHIT),
172 CRASHTYPE(PPC_RADIX_TLBIEL),
173};
174
175struct crashtype_category powerpc_crashtypes = {
176 .crashtypes = crashtypes,
177 .len = ARRAY_SIZE(crashtypes),
178};