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 /* Inserts new slb entries */
insert_slb_entry(unsigned long p,int ssize,int page_size)9 static void insert_slb_entry(unsigned long p, int ssize, int page_size)
10 {
11 unsigned long flags;
12
13 flags = SLB_VSID_KERNEL | mmu_psize_defs[page_size].sllp;
14 preempt_disable();
15
16 asm volatile("slbmte %0,%1" :
17 : "r" (mk_vsid_data(p, ssize, flags)),
18 "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED))
19 : "memory");
20
21 asm volatile("slbmte %0,%1" :
22 : "r" (mk_vsid_data(p, ssize, flags)),
23 "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED + 1))
24 : "memory");
25 preempt_enable();
26 }
27
28 /* Inject slb multihit on vmalloc-ed address i.e 0xD00... */
inject_vmalloc_slb_multihit(void)29 static int inject_vmalloc_slb_multihit(void)
30 {
31 char *p;
32
33 p = vmalloc(PAGE_SIZE);
34 if (!p)
35 return -ENOMEM;
36
37 insert_slb_entry((unsigned long)p, MMU_SEGSIZE_1T, mmu_vmalloc_psize);
38 /*
39 * This triggers exception, If handled correctly we must recover
40 * from this error.
41 */
42 p[0] = '!';
43 vfree(p);
44 return 0;
45 }
46
47 /* Inject slb multihit on kmalloc-ed address i.e 0xC00... */
inject_kmalloc_slb_multihit(void)48 static int inject_kmalloc_slb_multihit(void)
49 {
50 char *p;
51
52 p = kmalloc(2048, GFP_KERNEL);
53 if (!p)
54 return -ENOMEM;
55
56 insert_slb_entry((unsigned long)p, MMU_SEGSIZE_1T, mmu_linear_psize);
57 /*
58 * This triggers exception, If handled correctly we must recover
59 * from this error.
60 */
61 p[0] = '!';
62 kfree(p);
63 return 0;
64 }
65
66 /*
67 * Few initial SLB entries are bolted. Add a test to inject
68 * multihit in bolted entry 0.
69 */
insert_dup_slb_entry_0(void)70 static void insert_dup_slb_entry_0(void)
71 {
72 unsigned long test_address = PAGE_OFFSET, *test_ptr;
73 unsigned long esid, vsid;
74 unsigned long i = 0;
75
76 test_ptr = (unsigned long *)test_address;
77 preempt_disable();
78
79 asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i));
80 asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i));
81
82 /* for i !=0 we would need to mask out the old entry number */
83 asm volatile("slbmte %0,%1" :
84 : "r" (vsid),
85 "r" (esid | SLB_NUM_BOLTED)
86 : "memory");
87
88 asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i));
89 asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i));
90
91 /* for i !=0 we would need to mask out the old entry number */
92 asm volatile("slbmte %0,%1" :
93 : "r" (vsid),
94 "r" (esid | (SLB_NUM_BOLTED + 1))
95 : "memory");
96
97 pr_info("%s accessing test address 0x%lx: 0x%lx\n",
98 __func__, test_address, *test_ptr);
99
100 preempt_enable();
101 }
102
lkdtm_PPC_SLB_MULTIHIT(void)103 static void lkdtm_PPC_SLB_MULTIHIT(void)
104 {
105 if (!radix_enabled()) {
106 pr_info("Injecting SLB multihit errors\n");
107 /*
108 * These need not be separate tests, And they do pretty
109 * much same thing. In any case we must recover from the
110 * errors introduced by these functions, machine would not
111 * survive these tests in case of failure to handle.
112 */
113 inject_vmalloc_slb_multihit();
114 inject_kmalloc_slb_multihit();
115 insert_dup_slb_entry_0();
116 pr_info("Recovered from SLB multihit errors\n");
117 } else {
118 pr_err("XFAIL: This test is for ppc64 and with hash mode MMU only\n");
119 }
120 }
121
122 static struct crashtype crashtypes[] = {
123 CRASHTYPE(PPC_SLB_MULTIHIT),
124 };
125
126 struct crashtype_category powerpc_crashtypes = {
127 .crashtypes = crashtypes,
128 .len = ARRAY_SIZE(crashtypes),
129 };
130