1 /* 2 * Transactional Memory Unit Tests 3 * 4 * Copyright 2016 Suraj Jitindar Singh, IBM. 5 * 6 * This work is licensed under the terms of the GNU LGPL, version 2. 7 */ 8 #include <libcflat.h> 9 #include <asm/hcall.h> 10 #include <asm/processor.h> 11 #include <asm/handlers.h> 12 #include <asm/smp.h> 13 #include <asm/setup.h> 14 #include <devicetree.h> 15 16 /* Check "ibm,pa-features" property of a CPU node for the TM flag */ 17 static void cpu_has_tm(int fdtnode, u64 regval __unused, void *ptr) 18 { 19 const struct fdt_property *prop; 20 int plen; 21 22 prop = fdt_get_property(dt_fdt(), fdtnode, "ibm,pa-features", &plen); 23 if (!prop) /* No features means TM is also not available */ 24 return; 25 /* Sanity check for the property layout (first two bytes are header) */ 26 assert(plen >= 8 && prop->data[1] == 0 && prop->data[0] <= plen - 2); 27 28 /* 29 * The "Transactional Memory Category Support" flags are at byte 30 * offset 22 and 23 of the attribute type 0, so when adding the 31 * two bytes for the header, we've got to look at offset 24 for 32 * the TM support bit. 33 */ 34 if (prop->data[0] >= 24 && (prop->data[24] & 0x80) != 0) 35 *(int *)ptr += 1; 36 } 37 38 /* Check amount of CPUs nodes that have the TM flag */ 39 static int count_cpus_with_tm(void) 40 { 41 int ret; 42 int available = 0; 43 44 ret = dt_for_each_cpu_node(cpu_has_tm, &available); 45 if (ret < 0) 46 return ret; 47 48 return available; 49 } 50 51 static int h_cede(void) 52 { 53 register uint64_t r3 asm("r3") = H_CEDE; 54 55 asm volatile ("sc 1" : "+r"(r3) : 56 : "r0", "r4", "r5", "r6", "r7", "r8", "r9", 57 "r10", "r11", "r12", "xer", "ctr", "cc"); 58 59 return r3; 60 } 61 62 /* 63 * Enable transactional memory 64 * Returns: FALSE - Failure 65 * TRUE - Success 66 */ 67 static bool enable_tm(void) 68 { 69 uint64_t msr = 0; 70 71 asm volatile ("mfmsr %[msr]" : [msr] "=r" (msr)); 72 73 msr |= (((uint64_t) 1) << 32); 74 75 asm volatile ("mtmsrd %[msr]\n\t" 76 "mfmsr %[msr]" : [msr] "+r" (msr)); 77 78 return !!(msr & (((uint64_t) 1) << 32)); 79 } 80 81 /* 82 * Test H_CEDE call while transactional memory transaction is suspended 83 * 84 * WARNING: This tests for a known vulnerability in which the host may go down. 85 * Probably best not to run this if your host going down is going to cause 86 * problems. 87 * 88 * If the test passes then your kernel probably has the necessary patch. 89 * If the test fails then the H_CEDE call was unsuccessful and the 90 * vulnerability wasn't tested. 91 * If the test hits the vulnerability then it will never complete or report and 92 * the qemu process will block indefinitely. RCU stalls will be detected on the 93 * cpu and any process scheduled on the lost cpu will also block indefinitely. 94 */ 95 static void test_h_cede_tm(int argc, char **argv) 96 { 97 int i; 98 static uint64_t decr = 0x3FFFFF; /* ~10ms */ 99 100 if (argc > 2) 101 report_abort("Unsupported argument: '%s'", argv[2]); 102 103 handle_exception(0x900, &dec_except_handler, &decr); 104 asm volatile ("mtdec %0" : : "r" (decr)); 105 106 if (!start_all_cpus(halt, 0)) 107 report_abort("Failed to start secondary cpus"); 108 109 if (!enable_tm()) 110 report_abort("Failed to enable tm"); 111 112 /* 113 * Begin a transaction and guarantee we are in the suspend state 114 * before continuing 115 */ 116 asm volatile ("1: .long 0x7c00051d\n\t" /* tbegin. */ 117 "beq 2f\n\t" 118 ".long 0x7c0005dd\n\t" /* tsuspend. */ 119 "2: .long 0x7c00059c\n\t" /* tcheck cr0 */ 120 "bf 2,1b" : : : "cr0"); 121 122 for (i = 0; i < 500; i++) { 123 uint64_t rval = h_cede(); 124 125 if (rval != H_SUCCESS) 126 break; 127 mdelay(5); 128 } 129 130 report(i == 500, "H_CEDE TM"); 131 } 132 133 struct { 134 const char *name; 135 void (*func)(int argc, char **argv); 136 } hctests[] = { 137 { "h_cede_tm", test_h_cede_tm }, 138 { NULL, NULL } 139 }; 140 141 int main(int argc, char **argv) 142 { 143 bool all; 144 int i, cpus_with_tm; 145 146 report_prefix_push("tm"); 147 148 cpus_with_tm = count_cpus_with_tm(); 149 if (cpus_with_tm == 0) { 150 report_skip("TM is not available"); 151 goto done; 152 } 153 report(cpus_with_tm == nr_cpus, 154 "TM available in all 'ibm,pa-features' properties"); 155 156 all = argc == 1 || !strcmp(argv[1], "all"); 157 158 for (i = 0; hctests[i].name != NULL; i++) { 159 if (all || strcmp(argv[1], hctests[i].name) == 0) { 160 report_prefix_push(hctests[i].name); 161 hctests[i].func(argc, argv); 162 report_prefix_pop(); 163 } 164 } 165 166 done: 167 report_prefix_pop(); 168 return report_summary(); 169 } 170