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