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