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