18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2019, Gustavo Romero, Michael Neuling, IBM Corp.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This test will spawn two processes. Both will be attached to the same
68c2ecf20Sopenharmony_ci * CPU (CPU 0). The child will be in a loop writing to FP register f31 and
78c2ecf20Sopenharmony_ci * VMX/VEC/Altivec register vr31 a known value, called poison, calling
88c2ecf20Sopenharmony_ci * sched_yield syscall after to allow the parent to switch on the CPU.
98c2ecf20Sopenharmony_ci * Parent will set f31 and vr31 to 1 and in a loop will check if f31 and
108c2ecf20Sopenharmony_ci * vr31 remain 1 as expected until a given timeout (2m). If the issue is
118c2ecf20Sopenharmony_ci * present child's poison will leak into parent's f31 or vr31 registers,
128c2ecf20Sopenharmony_ci * otherwise, poison will never leak into parent's f31 and vr31 registers.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define _GNU_SOURCE
168c2ecf20Sopenharmony_ci#include <stdio.h>
178c2ecf20Sopenharmony_ci#include <stdlib.h>
188c2ecf20Sopenharmony_ci#include <unistd.h>
198c2ecf20Sopenharmony_ci#include <inttypes.h>
208c2ecf20Sopenharmony_ci#include <sched.h>
218c2ecf20Sopenharmony_ci#include <sys/types.h>
228c2ecf20Sopenharmony_ci#include <signal.h>
238c2ecf20Sopenharmony_ci#include <inttypes.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "tm.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciint tm_poison_test(void)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	int cpu, pid;
308c2ecf20Sopenharmony_ci	cpu_set_t cpuset;
318c2ecf20Sopenharmony_ci	uint64_t poison = 0xdeadbeefc0dec0fe;
328c2ecf20Sopenharmony_ci	uint64_t unknown = 0;
338c2ecf20Sopenharmony_ci	bool fail_fp = false;
348c2ecf20Sopenharmony_ci	bool fail_vr = false;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	SKIP_IF(!have_htm());
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	cpu = pick_online_cpu();
398c2ecf20Sopenharmony_ci	FAIL_IF(cpu < 0);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	// Attach both Child and Parent to the same CPU
428c2ecf20Sopenharmony_ci	CPU_ZERO(&cpuset);
438c2ecf20Sopenharmony_ci	CPU_SET(cpu, &cpuset);
448c2ecf20Sopenharmony_ci	FAIL_IF(sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	pid = fork();
478c2ecf20Sopenharmony_ci	if (!pid) {
488c2ecf20Sopenharmony_ci		/**
498c2ecf20Sopenharmony_ci		 * child
508c2ecf20Sopenharmony_ci		 */
518c2ecf20Sopenharmony_ci		while (1) {
528c2ecf20Sopenharmony_ci			sched_yield();
538c2ecf20Sopenharmony_ci			asm (
548c2ecf20Sopenharmony_ci				"mtvsrd 31, %[poison];" // f31 = poison
558c2ecf20Sopenharmony_ci				"mtvsrd 63, %[poison];" // vr31 = poison
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci				: : [poison] "r" (poison) : );
588c2ecf20Sopenharmony_ci		}
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	/**
628c2ecf20Sopenharmony_ci	 * parent
638c2ecf20Sopenharmony_ci	 */
648c2ecf20Sopenharmony_ci	asm (
658c2ecf20Sopenharmony_ci		/*
668c2ecf20Sopenharmony_ci		 * Set r3, r4, and f31 to known value 1 before entering
678c2ecf20Sopenharmony_ci		 * in transaction. They won't be written after that.
688c2ecf20Sopenharmony_ci		 */
698c2ecf20Sopenharmony_ci		"       li      3, 0x1          ;"
708c2ecf20Sopenharmony_ci		"       li      4, 0x1          ;"
718c2ecf20Sopenharmony_ci		"       mtvsrd  31, 4           ;"
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		/*
748c2ecf20Sopenharmony_ci		 * The Time Base (TB) is a 64-bit counter register that is
758c2ecf20Sopenharmony_ci		 * independent of the CPU clock and which is incremented
768c2ecf20Sopenharmony_ci		 * at a frequency of 512000000 Hz, so every 1.953125ns.
778c2ecf20Sopenharmony_ci		 * So it's necessary 120s/0.000000001953125s = 61440000000
788c2ecf20Sopenharmony_ci		 * increments to get a 2 minutes timeout. Below we set that
798c2ecf20Sopenharmony_ci		 * value in r5 and then use r6 to track initial TB value,
808c2ecf20Sopenharmony_ci		 * updating TB values in r7 at every iteration and comparing it
818c2ecf20Sopenharmony_ci		 * to r6. When r7 (current) - r6 (initial) > 61440000000 we bail
828c2ecf20Sopenharmony_ci		 * out since for sure we spent already 2 minutes in the loop.
838c2ecf20Sopenharmony_ci		 * SPR 268 is the TB register.
848c2ecf20Sopenharmony_ci		 */
858c2ecf20Sopenharmony_ci		"       lis     5, 14           ;"
868c2ecf20Sopenharmony_ci		"       ori     5, 5, 19996     ;"
878c2ecf20Sopenharmony_ci		"       sldi    5, 5, 16        ;" // r5 = 61440000000
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		"       mfspr   6, 268          ;" // r6 (TB initial)
908c2ecf20Sopenharmony_ci		"1:     mfspr   7, 268          ;" // r7 (TB current)
918c2ecf20Sopenharmony_ci		"       subf    7, 6, 7         ;" // r7 - r6 > 61440000000 ?
928c2ecf20Sopenharmony_ci		"       cmpd    7, 5            ;"
938c2ecf20Sopenharmony_ci		"       bgt     3f              ;" // yes, exit
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		/*
968c2ecf20Sopenharmony_ci		 * Main loop to check f31
978c2ecf20Sopenharmony_ci		 */
988c2ecf20Sopenharmony_ci		"       tbegin.                 ;" // no, try again
998c2ecf20Sopenharmony_ci		"       beq     1b              ;" // restart if no timeout
1008c2ecf20Sopenharmony_ci		"       mfvsrd  3, 31           ;" // read f31
1018c2ecf20Sopenharmony_ci		"       cmpd    3, 4            ;" // f31 == 1 ?
1028c2ecf20Sopenharmony_ci		"       bne     2f              ;" // broken :-(
1038c2ecf20Sopenharmony_ci		"       tabort. 3               ;" // try another transaction
1048c2ecf20Sopenharmony_ci		"2:     tend.                   ;" // commit transaction
1058c2ecf20Sopenharmony_ci		"3:     mr    %[unknown], 3     ;" // record r3
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		: [unknown] "=r" (unknown)
1088c2ecf20Sopenharmony_ci		:
1098c2ecf20Sopenharmony_ci		: "cr0", "r3", "r4", "r5", "r6", "r7", "vs31"
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * On leak 'unknown' will contain 'poison' value from child,
1158c2ecf20Sopenharmony_ci	 * otherwise (no leak) 'unknown' will contain the same value
1168c2ecf20Sopenharmony_ci	 * as r3 before entering in transactional mode, i.e. 0x1.
1178c2ecf20Sopenharmony_ci	 */
1188c2ecf20Sopenharmony_ci	fail_fp = unknown != 0x1;
1198c2ecf20Sopenharmony_ci	if (fail_fp)
1208c2ecf20Sopenharmony_ci		printf("Unknown value %#"PRIx64" leaked into f31!\n", unknown);
1218c2ecf20Sopenharmony_ci	else
1228c2ecf20Sopenharmony_ci		printf("Good, no poison or leaked value into FP registers\n");
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	asm (
1258c2ecf20Sopenharmony_ci		/*
1268c2ecf20Sopenharmony_ci		 * Set r3, r4, and vr31 to known value 1 before entering
1278c2ecf20Sopenharmony_ci		 * in transaction. They won't be written after that.
1288c2ecf20Sopenharmony_ci		 */
1298c2ecf20Sopenharmony_ci		"       li      3, 0x1          ;"
1308c2ecf20Sopenharmony_ci		"       li      4, 0x1          ;"
1318c2ecf20Sopenharmony_ci		"       mtvsrd  63, 4           ;"
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		"       lis     5, 14           ;"
1348c2ecf20Sopenharmony_ci		"       ori     5, 5, 19996     ;"
1358c2ecf20Sopenharmony_ci		"       sldi    5, 5, 16        ;" // r5 = 61440000000
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		"       mfspr   6, 268          ;" // r6 (TB initial)
1388c2ecf20Sopenharmony_ci		"1:     mfspr   7, 268          ;" // r7 (TB current)
1398c2ecf20Sopenharmony_ci		"       subf    7, 6, 7         ;" // r7 - r6 > 61440000000 ?
1408c2ecf20Sopenharmony_ci		"       cmpd    7, 5            ;"
1418c2ecf20Sopenharmony_ci		"       bgt     3f              ;" // yes, exit
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		/*
1448c2ecf20Sopenharmony_ci		 * Main loop to check vr31
1458c2ecf20Sopenharmony_ci		 */
1468c2ecf20Sopenharmony_ci		"       tbegin.                 ;" // no, try again
1478c2ecf20Sopenharmony_ci		"       beq     1b              ;" // restart if no timeout
1488c2ecf20Sopenharmony_ci		"       mfvsrd  3, 63           ;" // read vr31
1498c2ecf20Sopenharmony_ci		"       cmpd    3, 4            ;" // vr31 == 1 ?
1508c2ecf20Sopenharmony_ci		"       bne     2f              ;" // broken :-(
1518c2ecf20Sopenharmony_ci		"       tabort. 3               ;" // try another transaction
1528c2ecf20Sopenharmony_ci		"2:     tend.                   ;" // commit transaction
1538c2ecf20Sopenharmony_ci		"3:     mr    %[unknown], 3     ;" // record r3
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		: [unknown] "=r" (unknown)
1568c2ecf20Sopenharmony_ci		:
1578c2ecf20Sopenharmony_ci		: "cr0", "r3", "r4", "r5", "r6", "r7", "vs63"
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/*
1628c2ecf20Sopenharmony_ci	 * On leak 'unknown' will contain 'poison' value from child,
1638c2ecf20Sopenharmony_ci	 * otherwise (no leak) 'unknown' will contain the same value
1648c2ecf20Sopenharmony_ci	 * as r3 before entering in transactional mode, i.e. 0x1.
1658c2ecf20Sopenharmony_ci	 */
1668c2ecf20Sopenharmony_ci	fail_vr = unknown != 0x1;
1678c2ecf20Sopenharmony_ci	if (fail_vr)
1688c2ecf20Sopenharmony_ci		printf("Unknown value %#"PRIx64" leaked into vr31!\n", unknown);
1698c2ecf20Sopenharmony_ci	else
1708c2ecf20Sopenharmony_ci		printf("Good, no poison or leaked value into VEC registers\n");
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	kill(pid, SIGKILL);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return (fail_fp | fail_vr);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ciint main(int argc, char *argv[])
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	/* Test completes in about 4m */
1808c2ecf20Sopenharmony_ci	test_harness_set_timeout(250);
1818c2ecf20Sopenharmony_ci	return test_harness(tm_poison_test, "tm_poison_test");
1828c2ecf20Sopenharmony_ci}
183