xref: /openbmc/linux/drivers/misc/lkdtm/powerpc.c (revision c4c3c32d)
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 */
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... */
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... */
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  */
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 
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