root/tools/testing/selftests/powerpc/tm/tm-poison.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tm_poison_test
  2. main

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

/* [<][>][^][v][top][bottom][index][help] */