Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/powerpc: Add a test of spectre_v2 mitigations

This test uses the PMU to count branch prediction hits/misses for a
known loop, and compare the result to the reported spectre v2
mitigation.

This gives us a way of sanity checking that the reported mitigation is
actually in effect.

Sample output for some cases, eg:

Power9:
sysfs reports: 'Vulnerable'
PM_BR_PRED_CCACHE: result 368 running/enabled 5792777124
PM_BR_MPRED_CCACHE: result 319 running/enabled 5792775546
PM_BR_PRED_PCACHE: result 2147483281 running/enabled 5792773128
PM_BR_MPRED_PCACHE: result 213604201 running/enabled 5792771640
Miss percent 9 %
OK - Measured branch prediction rates match reported spectre v2 mitigation.

sysfs reports: 'Mitigation: Indirect branch serialisation (kernel only)'
PM_BR_PRED_CCACHE: result 895 running/enabled 5780320920
PM_BR_MPRED_CCACHE: result 822 running/enabled 5780312414
PM_BR_PRED_PCACHE: result 2147482754 running/enabled 5780308836
PM_BR_MPRED_PCACHE: result 213639731 running/enabled 5780307912
Miss percent 9 %
OK - Measured branch prediction rates match reported spectre v2 mitigation.

sysfs reports: 'Mitigation: Indirect branch cache disabled'
PM_BR_PRED_CCACHE: result 2147483649 running/enabled 20540186160
PM_BR_MPRED_CCACHE: result 2147483649 running/enabled 20540180056
PM_BR_PRED_PCACHE: result 0 running/enabled 20540176090
PM_BR_MPRED_PCACHE: result 0 running/enabled 20540174182
Miss percent 100 %
OK - Measured branch prediction rates match reported spectre v2 mitigation.

Power8:
sysfs reports: 'Vulnerable'
PM_BR_PRED_CCACHE: result 2147483649 running/enabled 3505888142
PM_BR_MPRED_CCACHE: result 9 running/enabled 3505882788
Miss percent 0 %
OK - Measured branch prediction rates match reported spectre v2 mitigation.

sysfs reports: 'Mitigation: Indirect branch cache disabled'
PM_BR_PRED_CCACHE: result 2147483649 running/enabled 16931421988
PM_BR_MPRED_CCACHE: result 2147483649 running/enabled 16931416478
Miss percent 100 %
OK - Measured branch prediction rates match reported spectre v2 mitigation.
success: spectre_v2

Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20190520105520.22274-1-mpe@ellerman.id.au

+323 -1
+1
tools/testing/selftests/powerpc/include/utils.h
··· 34 34 35 35 int read_debugfs_file(char *debugfs_file, int *result); 36 36 int write_debugfs_file(char *debugfs_file, int result); 37 + int read_sysfs_file(char *debugfs_file, char *result, size_t result_size); 37 38 void set_dscr(unsigned long val); 38 39 int perf_event_open_counter(unsigned int type, 39 40 unsigned long config, int group_fd);
+2 -1
tools/testing/selftests/powerpc/security/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0+ 2 2 3 - TEST_GEN_PROGS := rfi_flush 3 + TEST_GEN_PROGS := rfi_flush spectre_v2 4 4 top_srcdir = ../../../../.. 5 5 6 6 CFLAGS += -I../../../../../usr/include ··· 8 8 include ../../lib.mk 9 9 10 10 $(TEST_GEN_PROGS): ../harness.c ../utils.c 11 + $(OUTPUT)/spectre_v2: ../pmu/event.c branch_loops.S
+82
tools/testing/selftests/powerpc/security/branch_loops.S
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + 3 + /* 4 + * Copyright 2019, Michael Ellerman, IBM Corp. 5 + */ 6 + 7 + #include <ppc-asm.h> 8 + 9 + .data 10 + 11 + jump_table: 12 + .long 0x0 13 + .long (.Lstate_1 - .Lstate_0) 14 + .long (.Lstate_2 - .Lstate_0) 15 + .long (.Lstate_3 - .Lstate_0) 16 + .long (.Lstate_4 - .Lstate_0) 17 + .long (.Lstate_5 - .Lstate_0) 18 + .long (.Lstate_6 - .Lstate_0) 19 + .long (.Lstate_7 - .Lstate_0) 20 + 21 + .text 22 + 23 + #define ITER_SHIFT 31 24 + 25 + .macro state number 26 + .balign 32 27 + .Lstate_\number: 28 + .if \number==7 29 + li r3, 0 30 + .else 31 + li r3, \number+1 32 + .endif 33 + b .Lloop 34 + .endm 35 + 36 + FUNC_START(pattern_cache_loop) 37 + li r3, 0 38 + li r4, 1 39 + sldi r4, r4, ITER_SHIFT 40 + 41 + .Lloop: cmpdi r4, 0 42 + beqlr 43 + 44 + addi r4, r4, -1 45 + 46 + ld r6, jump_table@got(%r2) 47 + sldi r5, r3, 2 48 + lwax r6, r5, r6 49 + ld r7, .Lstate_0@got(%r2) 50 + add r6, r6, r7 51 + mtctr r6 52 + bctr 53 + 54 + state 0 55 + state 1 56 + state 2 57 + state 3 58 + state 4 59 + state 5 60 + state 6 61 + state 7 62 + 63 + FUNC_END(pattern_cache_loop) 64 + 65 + 66 + FUNC_START(indirect_branch_loop) 67 + li r3, 1 68 + sldi r3, r3, ITER_SHIFT 69 + 70 + 1: cmpdi r3, 0 71 + beqlr 72 + 73 + addi r3, r3, -1 74 + 75 + ld r4, 2f@got(%r2) 76 + mtctr r4 77 + bctr 78 + 79 + .balign 32 80 + 2: b 1b 81 + 82 + FUNC_END(indirect_branch_loop)
+218
tools/testing/selftests/powerpc/security/spectre_v2.c
··· 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 + 22 + extern void pattern_cache_loop(void); 23 + extern void indirect_branch_loop(void); 24 + 25 + static 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 + 64 + static 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 + 74 + enum 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 + 85 + static 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 + 130 + int 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 + state = get_sysfs_state(); 138 + if (state == UNKNOWN) { 139 + printf("Error: couldn't determine spectre_v2 mitigation state?\n"); 140 + return -1; 141 + } 142 + 143 + memset(events, 0, sizeof(events)); 144 + 145 + setup_event(&events[0], PM_BR_PRED_CCACHE, "PM_BR_PRED_CCACHE"); 146 + setup_event(&events[1], PM_BR_MPRED_CCACHE, "PM_BR_MPRED_CCACHE"); 147 + FAIL_IF(event_open(&events[0])); 148 + FAIL_IF(event_open_with_group(&events[1], events[0].fd) == -1); 149 + 150 + is_p9 = ((mfspr(SPRN_PVR) >> 16) & 0xFFFF) == 0x4e; 151 + 152 + if (is_p9) { 153 + // Count pattern cache too 154 + setup_event(&events[2], PM_BR_PRED_PCACHE, "PM_BR_PRED_PCACHE"); 155 + setup_event(&events[3], PM_BR_MPRED_PCACHE, "PM_BR_MPRED_PCACHE"); 156 + 157 + FAIL_IF(event_open_with_group(&events[2], events[0].fd) == -1); 158 + FAIL_IF(event_open_with_group(&events[3], events[0].fd) == -1); 159 + } 160 + 161 + FAIL_IF(do_count_loop(events, is_p9, &miss_percent)); 162 + 163 + event_report_justified(&events[0], 18, 10); 164 + event_report_justified(&events[1], 18, 10); 165 + event_close(&events[0]); 166 + event_close(&events[1]); 167 + 168 + if (is_p9) { 169 + event_report_justified(&events[2], 18, 10); 170 + event_report_justified(&events[3], 18, 10); 171 + event_close(&events[2]); 172 + event_close(&events[3]); 173 + } 174 + 175 + printf("Miss percent %lld %%\n", miss_percent); 176 + 177 + switch (state) { 178 + case VULNERABLE: 179 + case NOT_AFFECTED: 180 + case COUNT_CACHE_FLUSH_SW: 181 + case COUNT_CACHE_FLUSH_HW: 182 + // These should all not affect userspace branch prediction 183 + if (miss_percent > 15) { 184 + printf("Branch misses > 15%% unexpected in this configuration!\n"); 185 + printf("Possible mis-match between reported & actual mitigation\n"); 186 + return 1; 187 + } 188 + break; 189 + case BRANCH_SERIALISATION: 190 + // This seems to affect userspace branch prediction a bit? 191 + if (miss_percent > 25) { 192 + printf("Branch misses > 25%% unexpected in this configuration!\n"); 193 + printf("Possible mis-match between reported & actual mitigation\n"); 194 + return 1; 195 + } 196 + break; 197 + case COUNT_CACHE_DISABLED: 198 + if (miss_percent < 95) { 199 + printf("Branch misses < 20%% unexpected in this configuration!\n"); 200 + printf("Possible mis-match between reported & actual mitigation\n"); 201 + return 1; 202 + } 203 + break; 204 + case UNKNOWN: 205 + case BTB_FLUSH: 206 + printf("Not sure!\n"); 207 + return 1; 208 + } 209 + 210 + printf("OK - Measured branch prediction rates match reported spectre v2 mitigation.\n"); 211 + 212 + return 0; 213 + } 214 + 215 + int main(int argc, char *argv[]) 216 + { 217 + return test_harness(spectre_v2_test, "spectre_v2"); 218 + }
+20
tools/testing/selftests/powerpc/utils.c
··· 127 127 return strcmp(uts.machine, "ppc64le") == 0; 128 128 } 129 129 130 + int read_sysfs_file(char *fpath, char *result, size_t result_size) 131 + { 132 + char path[PATH_MAX] = "/sys/"; 133 + int rc = -1, fd; 134 + 135 + strncat(path, fpath, PATH_MAX - strlen(path) - 1); 136 + 137 + if ((fd = open(path, O_RDONLY)) < 0) 138 + return rc; 139 + 140 + rc = read(fd, result, result_size); 141 + 142 + close(fd); 143 + 144 + if (rc < 0) 145 + return rc; 146 + 147 + return 0; 148 + } 149 + 130 150 int read_debugfs_file(char *debugfs_file, int *result) 131 151 { 132 152 int rc = -1, fd;