xref: /kvm-unit-tests/powerpc/tm.c (revision dfc1fec2fbde04ad607e1aed560cf7059350c70f) !
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 /*
52  * Enable transactional memory
53  * Returns:	FALSE - Failure
54  *		TRUE - Success
55  */
56 static bool enable_tm(void)
57 {
58 	uint64_t msr = 0;
59 
60 	asm volatile ("mfmsr %[msr]" : [msr] "=r" (msr));
61 
62 	msr |= (((uint64_t) 1) << 32);
63 
64 	asm volatile ("mtmsrd %[msr]\n\t"
65 		      "mfmsr %[msr]" : [msr] "+r" (msr));
66 
67 	return !!(msr & (((uint64_t) 1) << 32));
68 }
69 
70 /*
71  * Test H_CEDE call while transactional memory transaction is suspended
72  *
73  * WARNING: This tests for a known vulnerability in which the host may go down.
74  * Probably best not to run this if your host going down is going to cause
75  * problems.
76  *
77  * If the test passes then your kernel probably has the necessary patch.
78  * If the test fails then the H_CEDE call was unsuccessful and the
79  * vulnerability wasn't tested.
80  * If the test hits the vulnerability then it will never complete or report and
81  * the qemu process will block indefinitely. RCU stalls will be detected on the
82  * cpu and any process scheduled on the lost cpu will also block indefinitely.
83  */
84 static void test_h_cede_tm(int argc, char **argv)
85 {
86 	int i;
87 
88 	if (argc > 2)
89 		report_abort("Unsupported argument: '%s'", argv[2]);
90 
91 	if (!start_all_cpus(halt, 0))
92 		report_abort("Failed to start secondary cpus");
93 
94 	if (!enable_tm())
95 		report_abort("Failed to enable tm");
96 
97 	/*
98 	 * Begin a transaction and guarantee we are in the suspend state
99 	 * before continuing
100 	 */
101 	asm volatile ("1: .long 0x7c00051d\n\t"	/* tbegin. */
102 		      "beq 2f\n\t"
103 		      ".long 0x7c0005dd\n\t"	/* tsuspend. */
104 		      "2: .long 0x7c00059c\n\t"	/* tcheck cr0 */
105 		      "bf 2,1b" : : : "cr0");
106 
107 	for (i = 0; i < 500; i++) {
108 		msleep(10);
109 		mdelay(5);
110 	}
111 
112 	report(i == 500, "H_CEDE TM");
113 }
114 
115 struct {
116 	const char *name;
117 	void (*func)(int argc, char **argv);
118 } hctests[] = {
119 	{ "h_cede_tm", test_h_cede_tm },
120 	{ NULL, NULL }
121 };
122 
123 int main(int argc, char **argv)
124 {
125 	bool all;
126 	int i, cpus_with_tm;
127 
128 	report_prefix_push("tm");
129 
130 	cpus_with_tm = count_cpus_with_tm();
131 	if (cpus_with_tm == 0) {
132 		report_skip("TM is not available");
133 		goto done;
134 	}
135 	report(cpus_with_tm == nr_cpus,
136 	       "TM available in all 'ibm,pa-features' properties");
137 
138 	all = argc == 1 || !strcmp(argv[1], "all");
139 
140 	for (i = 0; hctests[i].name != NULL; i++) {
141 		if (all || strcmp(argv[1], hctests[i].name) == 0) {
142 			report_prefix_push(hctests[i].name);
143 			hctests[i].func(argc, argv);
144 			report_prefix_pop();
145 		}
146 	}
147 
148 done:
149 	report_prefix_pop();
150 	return report_summary();
151 }
152