| 1 | |
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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
|
| 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;
|
| 122 | r = 1;
|
| 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 | };
|