xref: /kvm-unit-tests/powerpc/tm.c (revision 2c96b77ec9d3b1fcec7525174e23a6240ee05949)
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