1
2
3
4
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
42
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,
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
95 buf[sizeof(buf) - 1] = '\0';
96
97
98 len = strlen(buf);
99 FAIL_IF(len < 1);
100 buf[len - 1] = '\0';
101
102 printf("sysfs reports: '%s'\n", buf);
103
104
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
124#define PM_BR_MPRED_CCACHE 0x040ac
125#define PM_BR_PRED_PCACHE 0x048a0
126#define PM_BR_MPRED_PCACHE 0x048b0
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 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
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
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
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
215int main(int argc, char *argv[])
216{
217 return test_harness(spectre_v2_test, "spectre_v2");
218}
219