linux/tools/testing/selftests/powerpc/security/spectre_v2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2
   3/*
   4 * Copyright 2018-2019 IBM Corporation.
   5 */
   6
   7#define __SANE_USERSPACE_TYPES__
   8
   9#include <sys/types.h>
  10#include <stdint.h>
  11#include <malloc.h>
  12#include <unistd.h>
  13#include <stdlib.h>
  14#include <string.h>
  15#include <stdio.h>
  16#include <sys/prctl.h>
  17#include "utils.h"
  18
  19#include "../pmu/event.h"
  20
  21
  22extern void pattern_cache_loop(void);
  23extern void indirect_branch_loop(void);
  24
  25static int do_count_loop(struct event *events, bool is_p9, s64 *miss_percent)
  26{
  27        u64 pred, mpred;
  28
  29        prctl(PR_TASK_PERF_EVENTS_ENABLE);
  30
  31        if (is_p9)
  32                pattern_cache_loop();
  33        else
  34                indirect_branch_loop();
  35
  36        prctl(PR_TASK_PERF_EVENTS_DISABLE);
  37
  38        event_read(&events[0]);
  39        event_read(&events[1]);
  40
  41        // We could scale all the events by running/enabled but we're lazy
  42        // As long as the PMU is uncontended they should all run
  43        FAIL_IF(events[0].result.running != events[0].result.enabled);
  44        FAIL_IF(events[1].result.running != events[1].result.enabled);
  45
  46        pred =  events[0].result.value;
  47        mpred = events[1].result.value;
  48
  49        if (is_p9) {
  50                event_read(&events[2]);
  51                event_read(&events[3]);
  52                FAIL_IF(events[2].result.running != events[2].result.enabled);
  53                FAIL_IF(events[3].result.running != events[3].result.enabled);
  54
  55                pred  += events[2].result.value;
  56                mpred += events[3].result.value;
  57        }
  58
  59        *miss_percent = 100 * mpred / pred;
  60
  61        return 0;
  62}
  63
  64static void setup_event(struct event *e, u64 config, char *name)
  65{
  66        event_init_named(e, config, name);
  67
  68        e->attr.disabled = 1;
  69        e->attr.exclude_kernel = 1;
  70        e->attr.exclude_hv = 1;
  71        e->attr.exclude_idle = 1;
  72}
  73
  74enum spectre_v2_state {
  75        VULNERABLE = 0,
  76        UNKNOWN = 1,            // Works with FAIL_IF()
  77        NOT_AFFECTED,
  78        BRANCH_SERIALISATION,
  79        COUNT_CACHE_DISABLED,
  80        COUNT_CACHE_FLUSH_SW,
  81        COUNT_CACHE_FLUSH_HW,
  82        BTB_FLUSH,
  83};
  84
  85static enum spectre_v2_state get_sysfs_state(void)
  86{
  87        enum spectre_v2_state state = UNKNOWN;
  88        char buf[256];
  89        int len;
  90
  91        memset(buf, 0, sizeof(buf));
  92        FAIL_IF(read_sysfs_file("devices/system/cpu/vulnerabilities/spectre_v2", buf, sizeof(buf)));
  93
  94        // Make sure it's NULL terminated
  95        buf[sizeof(buf) - 1] = '\0';
  96
  97        // Trim the trailing newline
  98        len = strlen(buf);
  99        FAIL_IF(len < 1);
 100        buf[len - 1] = '\0';
 101
 102        printf("sysfs reports: '%s'\n", buf);
 103
 104        // Order matters
 105        if (strstr(buf, "Vulnerable"))
 106                state = VULNERABLE;
 107        else if (strstr(buf, "Not affected"))
 108                state = NOT_AFFECTED;
 109        else if (strstr(buf, "Indirect branch serialisation (kernel only)"))
 110                state = BRANCH_SERIALISATION;
 111        else if (strstr(buf, "Indirect branch cache disabled"))
 112                state = COUNT_CACHE_DISABLED;
 113        else if (strstr(buf, "Software count cache flush (hardware accelerated)"))
 114                state = COUNT_CACHE_FLUSH_HW;
 115        else if (strstr(buf, "Software count cache flush"))
 116                state = COUNT_CACHE_FLUSH_SW;
 117        else if (strstr(buf, "Branch predictor state flush"))
 118                state = BTB_FLUSH;
 119
 120        return state;
 121}
 122
 123#define PM_BR_PRED_CCACHE       0x040a4 // P8 + P9
 124#define PM_BR_MPRED_CCACHE      0x040ac // P8 + P9
 125#define PM_BR_PRED_PCACHE       0x048a0 // P9 only
 126#define PM_BR_MPRED_PCACHE      0x048b0 // P9 only
 127
 128#define SPRN_PVR 287
 129
 130int spectre_v2_test(void)
 131{
 132        enum spectre_v2_state state;
 133        struct event events[4];
 134        s64 miss_percent;
 135        bool is_p9;
 136
 137        // The PMU events we use only work on Power8 or later
 138        SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
 139
 140        state = get_sysfs_state();
 141        if (state == UNKNOWN) {
 142                printf("Error: couldn't determine spectre_v2 mitigation state?\n");
 143                return -1;
 144        }
 145
 146        memset(events, 0, sizeof(events));
 147
 148        setup_event(&events[0], PM_BR_PRED_CCACHE,  "PM_BR_PRED_CCACHE");
 149        setup_event(&events[1], PM_BR_MPRED_CCACHE, "PM_BR_MPRED_CCACHE");
 150        FAIL_IF(event_open(&events[0]));
 151        FAIL_IF(event_open_with_group(&events[1], events[0].fd) == -1);
 152
 153        is_p9 = ((mfspr(SPRN_PVR) >>  16) & 0xFFFF) == 0x4e;
 154
 155        if (is_p9) {
 156                // Count pattern cache too
 157                setup_event(&events[2], PM_BR_PRED_PCACHE,  "PM_BR_PRED_PCACHE");
 158                setup_event(&events[3], PM_BR_MPRED_PCACHE, "PM_BR_MPRED_PCACHE");
 159
 160                FAIL_IF(event_open_with_group(&events[2], events[0].fd) == -1);
 161                FAIL_IF(event_open_with_group(&events[3], events[0].fd) == -1);
 162        }
 163
 164        FAIL_IF(do_count_loop(events, is_p9, &miss_percent));
 165
 166        event_report_justified(&events[0], 18, 10);
 167        event_report_justified(&events[1], 18, 10);
 168        event_close(&events[0]);
 169        event_close(&events[1]);
 170
 171        if (is_p9) {
 172                event_report_justified(&events[2], 18, 10);
 173                event_report_justified(&events[3], 18, 10);
 174                event_close(&events[2]);
 175                event_close(&events[3]);
 176        }
 177
 178        printf("Miss percent %lld %%\n", miss_percent);
 179
 180        switch (state) {
 181        case VULNERABLE:
 182        case NOT_AFFECTED:
 183        case COUNT_CACHE_FLUSH_SW:
 184        case COUNT_CACHE_FLUSH_HW:
 185                // These should all not affect userspace branch prediction
 186                if (miss_percent > 15) {
 187                        printf("Branch misses > 15%% unexpected in this configuration!\n");
 188                        printf("Possible mis-match between reported & actual mitigation\n");
 189                        /*
 190                         * Such a mismatch may be caused by a guest system
 191                         * reporting as vulnerable when the host is mitigated.
 192                         * Return skip code to avoid detecting this as an error.
 193                         * We are not vulnerable and reporting otherwise, so
 194                         * missing such a mismatch is safe.
 195                         */
 196                        if (state == VULNERABLE)
 197                                return 4;
 198
 199                        return 1;
 200                }
 201                break;
 202        case BRANCH_SERIALISATION:
 203                // This seems to affect userspace branch prediction a bit?
 204                if (miss_percent > 25) {
 205                        printf("Branch misses > 25%% unexpected in this configuration!\n");
 206                        printf("Possible mis-match between reported & actual mitigation\n");
 207                        return 1;
 208                }
 209                break;
 210        case COUNT_CACHE_DISABLED:
 211                if (miss_percent < 95) {
 212                        printf("Branch misses < 20%% unexpected in this configuration!\n");
 213                        printf("Possible mis-match between reported & actual mitigation\n");
 214                        return 1;
 215                }
 216                break;
 217        case UNKNOWN:
 218        case BTB_FLUSH:
 219                printf("Not sure!\n");
 220                return 1;
 221        }
 222
 223        printf("OK - Measured branch prediction rates match reported spectre v2 mitigation.\n");
 224
 225        return 0;
 226}
 227
 228int main(int argc, char *argv[])
 229{
 230        return test_harness(spectre_v2_test, "spectre_v2");
 231}
 232