162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2019, Gustavo Romero, Michael Neuling, IBM Corp. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This test will spawn two processes. Both will be attached to the same 662306a36Sopenharmony_ci * CPU (CPU 0). The child will be in a loop writing to FP register f31 and 762306a36Sopenharmony_ci * VMX/VEC/Altivec register vr31 a known value, called poison, calling 862306a36Sopenharmony_ci * sched_yield syscall after to allow the parent to switch on the CPU. 962306a36Sopenharmony_ci * Parent will set f31 and vr31 to 1 and in a loop will check if f31 and 1062306a36Sopenharmony_ci * vr31 remain 1 as expected until a given timeout (2m). If the issue is 1162306a36Sopenharmony_ci * present child's poison will leak into parent's f31 or vr31 registers, 1262306a36Sopenharmony_ci * otherwise, poison will never leak into parent's f31 and vr31 registers. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define _GNU_SOURCE 1662306a36Sopenharmony_ci#include <stdio.h> 1762306a36Sopenharmony_ci#include <stdlib.h> 1862306a36Sopenharmony_ci#include <unistd.h> 1962306a36Sopenharmony_ci#include <inttypes.h> 2062306a36Sopenharmony_ci#include <sched.h> 2162306a36Sopenharmony_ci#include <sys/types.h> 2262306a36Sopenharmony_ci#include <signal.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "tm.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciint tm_poison_test(void) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci int cpu, pid; 2962306a36Sopenharmony_ci cpu_set_t cpuset; 3062306a36Sopenharmony_ci uint64_t poison = 0xdeadbeefc0dec0fe; 3162306a36Sopenharmony_ci uint64_t unknown = 0; 3262306a36Sopenharmony_ci bool fail_fp = false; 3362306a36Sopenharmony_ci bool fail_vr = false; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci SKIP_IF(!have_htm()); 3662306a36Sopenharmony_ci SKIP_IF(htm_is_synthetic()); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci cpu = pick_online_cpu(); 3962306a36Sopenharmony_ci FAIL_IF(cpu < 0); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci // Attach both Child and Parent to the same CPU 4262306a36Sopenharmony_ci CPU_ZERO(&cpuset); 4362306a36Sopenharmony_ci CPU_SET(cpu, &cpuset); 4462306a36Sopenharmony_ci FAIL_IF(sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci pid = fork(); 4762306a36Sopenharmony_ci if (!pid) { 4862306a36Sopenharmony_ci /** 4962306a36Sopenharmony_ci * child 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci while (1) { 5262306a36Sopenharmony_ci sched_yield(); 5362306a36Sopenharmony_ci asm ( 5462306a36Sopenharmony_ci "mtvsrd 31, %[poison];" // f31 = poison 5562306a36Sopenharmony_ci "mtvsrd 63, %[poison];" // vr31 = poison 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci : : [poison] "r" (poison) : ); 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /** 6262306a36Sopenharmony_ci * parent 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci asm ( 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * Set r3, r4, and f31 to known value 1 before entering 6762306a36Sopenharmony_ci * in transaction. They won't be written after that. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci " li 3, 0x1 ;" 7062306a36Sopenharmony_ci " li 4, 0x1 ;" 7162306a36Sopenharmony_ci " mtvsrd 31, 4 ;" 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* 7462306a36Sopenharmony_ci * The Time Base (TB) is a 64-bit counter register that is 7562306a36Sopenharmony_ci * independent of the CPU clock and which is incremented 7662306a36Sopenharmony_ci * at a frequency of 512000000 Hz, so every 1.953125ns. 7762306a36Sopenharmony_ci * So it's necessary 120s/0.000000001953125s = 61440000000 7862306a36Sopenharmony_ci * increments to get a 2 minutes timeout. Below we set that 7962306a36Sopenharmony_ci * value in r5 and then use r6 to track initial TB value, 8062306a36Sopenharmony_ci * updating TB values in r7 at every iteration and comparing it 8162306a36Sopenharmony_ci * to r6. When r7 (current) - r6 (initial) > 61440000000 we bail 8262306a36Sopenharmony_ci * out since for sure we spent already 2 minutes in the loop. 8362306a36Sopenharmony_ci * SPR 268 is the TB register. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci " lis 5, 14 ;" 8662306a36Sopenharmony_ci " ori 5, 5, 19996 ;" 8762306a36Sopenharmony_ci " sldi 5, 5, 16 ;" // r5 = 61440000000 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci " mfspr 6, 268 ;" // r6 (TB initial) 9062306a36Sopenharmony_ci "1: mfspr 7, 268 ;" // r7 (TB current) 9162306a36Sopenharmony_ci " subf 7, 6, 7 ;" // r7 - r6 > 61440000000 ? 9262306a36Sopenharmony_ci " cmpd 7, 5 ;" 9362306a36Sopenharmony_ci " bgt 3f ;" // yes, exit 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* 9662306a36Sopenharmony_ci * Main loop to check f31 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci " tbegin. ;" // no, try again 9962306a36Sopenharmony_ci " beq 1b ;" // restart if no timeout 10062306a36Sopenharmony_ci " mfvsrd 3, 31 ;" // read f31 10162306a36Sopenharmony_ci " cmpd 3, 4 ;" // f31 == 1 ? 10262306a36Sopenharmony_ci " bne 2f ;" // broken :-( 10362306a36Sopenharmony_ci " tabort. 3 ;" // try another transaction 10462306a36Sopenharmony_ci "2: tend. ;" // commit transaction 10562306a36Sopenharmony_ci "3: mr %[unknown], 3 ;" // record r3 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci : [unknown] "=r" (unknown) 10862306a36Sopenharmony_ci : 10962306a36Sopenharmony_ci : "cr0", "r3", "r4", "r5", "r6", "r7", "vs31" 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci ); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* 11462306a36Sopenharmony_ci * On leak 'unknown' will contain 'poison' value from child, 11562306a36Sopenharmony_ci * otherwise (no leak) 'unknown' will contain the same value 11662306a36Sopenharmony_ci * as r3 before entering in transactional mode, i.e. 0x1. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci fail_fp = unknown != 0x1; 11962306a36Sopenharmony_ci if (fail_fp) 12062306a36Sopenharmony_ci printf("Unknown value %#"PRIx64" leaked into f31!\n", unknown); 12162306a36Sopenharmony_ci else 12262306a36Sopenharmony_ci printf("Good, no poison or leaked value into FP registers\n"); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci asm ( 12562306a36Sopenharmony_ci /* 12662306a36Sopenharmony_ci * Set r3, r4, and vr31 to known value 1 before entering 12762306a36Sopenharmony_ci * in transaction. They won't be written after that. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci " li 3, 0x1 ;" 13062306a36Sopenharmony_ci " li 4, 0x1 ;" 13162306a36Sopenharmony_ci " mtvsrd 63, 4 ;" 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci " lis 5, 14 ;" 13462306a36Sopenharmony_ci " ori 5, 5, 19996 ;" 13562306a36Sopenharmony_ci " sldi 5, 5, 16 ;" // r5 = 61440000000 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci " mfspr 6, 268 ;" // r6 (TB initial) 13862306a36Sopenharmony_ci "1: mfspr 7, 268 ;" // r7 (TB current) 13962306a36Sopenharmony_ci " subf 7, 6, 7 ;" // r7 - r6 > 61440000000 ? 14062306a36Sopenharmony_ci " cmpd 7, 5 ;" 14162306a36Sopenharmony_ci " bgt 3f ;" // yes, exit 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * Main loop to check vr31 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci " tbegin. ;" // no, try again 14762306a36Sopenharmony_ci " beq 1b ;" // restart if no timeout 14862306a36Sopenharmony_ci " mfvsrd 3, 63 ;" // read vr31 14962306a36Sopenharmony_ci " cmpd 3, 4 ;" // vr31 == 1 ? 15062306a36Sopenharmony_ci " bne 2f ;" // broken :-( 15162306a36Sopenharmony_ci " tabort. 3 ;" // try another transaction 15262306a36Sopenharmony_ci "2: tend. ;" // commit transaction 15362306a36Sopenharmony_ci "3: mr %[unknown], 3 ;" // record r3 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci : [unknown] "=r" (unknown) 15662306a36Sopenharmony_ci : 15762306a36Sopenharmony_ci : "cr0", "r3", "r4", "r5", "r6", "r7", "vs63" 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * On leak 'unknown' will contain 'poison' value from child, 16362306a36Sopenharmony_ci * otherwise (no leak) 'unknown' will contain the same value 16462306a36Sopenharmony_ci * as r3 before entering in transactional mode, i.e. 0x1. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci fail_vr = unknown != 0x1; 16762306a36Sopenharmony_ci if (fail_vr) 16862306a36Sopenharmony_ci printf("Unknown value %#"PRIx64" leaked into vr31!\n", unknown); 16962306a36Sopenharmony_ci else 17062306a36Sopenharmony_ci printf("Good, no poison or leaked value into VEC registers\n"); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci kill(pid, SIGKILL); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return (fail_fp | fail_vr); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciint main(int argc, char *argv[]) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci /* Test completes in about 4m */ 18062306a36Sopenharmony_ci test_harness_set_timeout(250); 18162306a36Sopenharmony_ci return test_harness(tm_poison_test, "tm_poison_test"); 18262306a36Sopenharmony_ci} 183