1a003365cSGustavo Romero // SPDX-License-Identifier: GPL-2.0-only
2a003365cSGustavo Romero /*
3a003365cSGustavo Romero * Copyright 2019, Gustavo Romero, Michael Neuling, IBM Corp.
4a003365cSGustavo Romero *
5a003365cSGustavo Romero * This test will spawn two processes. Both will be attached to the same
6a003365cSGustavo Romero * CPU (CPU 0). The child will be in a loop writing to FP register f31 and
7a003365cSGustavo Romero * VMX/VEC/Altivec register vr31 a known value, called poison, calling
8a003365cSGustavo Romero * sched_yield syscall after to allow the parent to switch on the CPU.
9a003365cSGustavo Romero * Parent will set f31 and vr31 to 1 and in a loop will check if f31 and
10a003365cSGustavo Romero * vr31 remain 1 as expected until a given timeout (2m). If the issue is
11a003365cSGustavo Romero * present child's poison will leak into parent's f31 or vr31 registers,
12a003365cSGustavo Romero * otherwise, poison will never leak into parent's f31 and vr31 registers.
13a003365cSGustavo Romero */
14a003365cSGustavo Romero
15a003365cSGustavo Romero #define _GNU_SOURCE
16a003365cSGustavo Romero #include <stdio.h>
17a003365cSGustavo Romero #include <stdlib.h>
18a003365cSGustavo Romero #include <unistd.h>
19a003365cSGustavo Romero #include <inttypes.h>
20a003365cSGustavo Romero #include <sched.h>
21a003365cSGustavo Romero #include <sys/types.h>
22a003365cSGustavo Romero #include <signal.h>
23a003365cSGustavo Romero
24a003365cSGustavo Romero #include "tm.h"
25a003365cSGustavo Romero
tm_poison_test(void)26a003365cSGustavo Romero int tm_poison_test(void)
27a003365cSGustavo Romero {
28c0176429SMichael Ellerman int cpu, pid;
29a003365cSGustavo Romero cpu_set_t cpuset;
30a003365cSGustavo Romero uint64_t poison = 0xdeadbeefc0dec0fe;
31a003365cSGustavo Romero uint64_t unknown = 0;
32a003365cSGustavo Romero bool fail_fp = false;
33a003365cSGustavo Romero bool fail_vr = false;
34a003365cSGustavo Romero
35a003365cSGustavo Romero SKIP_IF(!have_htm());
36*e42edf9bSJordan Niethe SKIP_IF(htm_is_synthetic());
37a003365cSGustavo Romero
38c0176429SMichael Ellerman cpu = pick_online_cpu();
39c0176429SMichael Ellerman FAIL_IF(cpu < 0);
40c0176429SMichael Ellerman
41c0176429SMichael Ellerman // Attach both Child and Parent to the same CPU
42a003365cSGustavo Romero CPU_ZERO(&cpuset);
43c0176429SMichael Ellerman CPU_SET(cpu, &cpuset);
44c0176429SMichael Ellerman FAIL_IF(sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0);
45a003365cSGustavo Romero
46a003365cSGustavo Romero pid = fork();
47a003365cSGustavo Romero if (!pid) {
48a003365cSGustavo Romero /**
49a003365cSGustavo Romero * child
50a003365cSGustavo Romero */
51a003365cSGustavo Romero while (1) {
52a003365cSGustavo Romero sched_yield();
53a003365cSGustavo Romero asm (
54a003365cSGustavo Romero "mtvsrd 31, %[poison];" // f31 = poison
55a003365cSGustavo Romero "mtvsrd 63, %[poison];" // vr31 = poison
56a003365cSGustavo Romero
57a003365cSGustavo Romero : : [poison] "r" (poison) : );
58a003365cSGustavo Romero }
59a003365cSGustavo Romero }
60a003365cSGustavo Romero
61a003365cSGustavo Romero /**
62a003365cSGustavo Romero * parent
63a003365cSGustavo Romero */
64a003365cSGustavo Romero asm (
65a003365cSGustavo Romero /*
66a003365cSGustavo Romero * Set r3, r4, and f31 to known value 1 before entering
67a003365cSGustavo Romero * in transaction. They won't be written after that.
68a003365cSGustavo Romero */
69a003365cSGustavo Romero " li 3, 0x1 ;"
70a003365cSGustavo Romero " li 4, 0x1 ;"
71a003365cSGustavo Romero " mtvsrd 31, 4 ;"
72a003365cSGustavo Romero
73a003365cSGustavo Romero /*
74a003365cSGustavo Romero * The Time Base (TB) is a 64-bit counter register that is
75a003365cSGustavo Romero * independent of the CPU clock and which is incremented
76a003365cSGustavo Romero * at a frequency of 512000000 Hz, so every 1.953125ns.
77a003365cSGustavo Romero * So it's necessary 120s/0.000000001953125s = 61440000000
78a003365cSGustavo Romero * increments to get a 2 minutes timeout. Below we set that
79a003365cSGustavo Romero * value in r5 and then use r6 to track initial TB value,
80a003365cSGustavo Romero * updating TB values in r7 at every iteration and comparing it
81a003365cSGustavo Romero * to r6. When r7 (current) - r6 (initial) > 61440000000 we bail
82a003365cSGustavo Romero * out since for sure we spent already 2 minutes in the loop.
83a003365cSGustavo Romero * SPR 268 is the TB register.
84a003365cSGustavo Romero */
85a003365cSGustavo Romero " lis 5, 14 ;"
86a003365cSGustavo Romero " ori 5, 5, 19996 ;"
87a003365cSGustavo Romero " sldi 5, 5, 16 ;" // r5 = 61440000000
88a003365cSGustavo Romero
89a003365cSGustavo Romero " mfspr 6, 268 ;" // r6 (TB initial)
90a003365cSGustavo Romero "1: mfspr 7, 268 ;" // r7 (TB current)
91a003365cSGustavo Romero " subf 7, 6, 7 ;" // r7 - r6 > 61440000000 ?
92a003365cSGustavo Romero " cmpd 7, 5 ;"
93a003365cSGustavo Romero " bgt 3f ;" // yes, exit
94a003365cSGustavo Romero
95a003365cSGustavo Romero /*
96a003365cSGustavo Romero * Main loop to check f31
97a003365cSGustavo Romero */
98a003365cSGustavo Romero " tbegin. ;" // no, try again
99a003365cSGustavo Romero " beq 1b ;" // restart if no timeout
100a003365cSGustavo Romero " mfvsrd 3, 31 ;" // read f31
101a003365cSGustavo Romero " cmpd 3, 4 ;" // f31 == 1 ?
102a003365cSGustavo Romero " bne 2f ;" // broken :-(
103a003365cSGustavo Romero " tabort. 3 ;" // try another transaction
104a003365cSGustavo Romero "2: tend. ;" // commit transaction
105a003365cSGustavo Romero "3: mr %[unknown], 3 ;" // record r3
106a003365cSGustavo Romero
107a003365cSGustavo Romero : [unknown] "=r" (unknown)
108a003365cSGustavo Romero :
109a003365cSGustavo Romero : "cr0", "r3", "r4", "r5", "r6", "r7", "vs31"
110a003365cSGustavo Romero
111a003365cSGustavo Romero );
112a003365cSGustavo Romero
113a003365cSGustavo Romero /*
114a003365cSGustavo Romero * On leak 'unknown' will contain 'poison' value from child,
115a003365cSGustavo Romero * otherwise (no leak) 'unknown' will contain the same value
116a003365cSGustavo Romero * as r3 before entering in transactional mode, i.e. 0x1.
117a003365cSGustavo Romero */
118a003365cSGustavo Romero fail_fp = unknown != 0x1;
119a003365cSGustavo Romero if (fail_fp)
120a003365cSGustavo Romero printf("Unknown value %#"PRIx64" leaked into f31!\n", unknown);
121a003365cSGustavo Romero else
122a003365cSGustavo Romero printf("Good, no poison or leaked value into FP registers\n");
123a003365cSGustavo Romero
124a003365cSGustavo Romero asm (
125a003365cSGustavo Romero /*
126a003365cSGustavo Romero * Set r3, r4, and vr31 to known value 1 before entering
127a003365cSGustavo Romero * in transaction. They won't be written after that.
128a003365cSGustavo Romero */
129a003365cSGustavo Romero " li 3, 0x1 ;"
130a003365cSGustavo Romero " li 4, 0x1 ;"
131a003365cSGustavo Romero " mtvsrd 63, 4 ;"
132a003365cSGustavo Romero
133a003365cSGustavo Romero " lis 5, 14 ;"
134a003365cSGustavo Romero " ori 5, 5, 19996 ;"
135a003365cSGustavo Romero " sldi 5, 5, 16 ;" // r5 = 61440000000
136a003365cSGustavo Romero
137a003365cSGustavo Romero " mfspr 6, 268 ;" // r6 (TB initial)
138a003365cSGustavo Romero "1: mfspr 7, 268 ;" // r7 (TB current)
139a003365cSGustavo Romero " subf 7, 6, 7 ;" // r7 - r6 > 61440000000 ?
140a003365cSGustavo Romero " cmpd 7, 5 ;"
141a003365cSGustavo Romero " bgt 3f ;" // yes, exit
142a003365cSGustavo Romero
143a003365cSGustavo Romero /*
144a003365cSGustavo Romero * Main loop to check vr31
145a003365cSGustavo Romero */
146a003365cSGustavo Romero " tbegin. ;" // no, try again
147a003365cSGustavo Romero " beq 1b ;" // restart if no timeout
148a003365cSGustavo Romero " mfvsrd 3, 63 ;" // read vr31
149a003365cSGustavo Romero " cmpd 3, 4 ;" // vr31 == 1 ?
150a003365cSGustavo Romero " bne 2f ;" // broken :-(
151a003365cSGustavo Romero " tabort. 3 ;" // try another transaction
152a003365cSGustavo Romero "2: tend. ;" // commit transaction
153a003365cSGustavo Romero "3: mr %[unknown], 3 ;" // record r3
154a003365cSGustavo Romero
155a003365cSGustavo Romero : [unknown] "=r" (unknown)
156a003365cSGustavo Romero :
157a003365cSGustavo Romero : "cr0", "r3", "r4", "r5", "r6", "r7", "vs63"
158a003365cSGustavo Romero
159a003365cSGustavo Romero );
160a003365cSGustavo Romero
161a003365cSGustavo Romero /*
162a003365cSGustavo Romero * On leak 'unknown' will contain 'poison' value from child,
163a003365cSGustavo Romero * otherwise (no leak) 'unknown' will contain the same value
164a003365cSGustavo Romero * as r3 before entering in transactional mode, i.e. 0x1.
165a003365cSGustavo Romero */
166a003365cSGustavo Romero fail_vr = unknown != 0x1;
167a003365cSGustavo Romero if (fail_vr)
168a003365cSGustavo Romero printf("Unknown value %#"PRIx64" leaked into vr31!\n", unknown);
169a003365cSGustavo Romero else
170a003365cSGustavo Romero printf("Good, no poison or leaked value into VEC registers\n");
171a003365cSGustavo Romero
172a003365cSGustavo Romero kill(pid, SIGKILL);
173a003365cSGustavo Romero
174a003365cSGustavo Romero return (fail_fp | fail_vr);
175a003365cSGustavo Romero }
176a003365cSGustavo Romero
main(int argc,char * argv[])177a003365cSGustavo Romero int main(int argc, char *argv[])
178a003365cSGustavo Romero {
179a003365cSGustavo Romero /* Test completes in about 4m */
180a003365cSGustavo Romero test_harness_set_timeout(250);
181a003365cSGustavo Romero return test_harness(tm_poison_test, "tm_poison_test");
182a003365cSGustavo Romero }
183