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
128int spectre_v2_test(void)
129{
130	enum spectre_v2_state state;
131	struct event events[4];
132	s64 miss_percent;
133	bool is_p9;
134
135	// The PMU events we use only work on Power8 or later
136	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
137
138	state = get_sysfs_state();
139	if (state == UNKNOWN) {
140		printf("Error: couldn't determine spectre_v2 mitigation state?\n");
141		return -1;
142	}
143
144	memset(events, 0, sizeof(events));
145
146	setup_event(&events[0], PM_BR_PRED_CCACHE,  "PM_BR_PRED_CCACHE");
147	setup_event(&events[1], PM_BR_MPRED_CCACHE, "PM_BR_MPRED_CCACHE");
148	FAIL_IF(event_open(&events[0]));
149	FAIL_IF(event_open_with_group(&events[1], events[0].fd) == -1);
150
151	is_p9 = ((mfspr(SPRN_PVR) >>  16) & 0xFFFF) == 0x4e;
152
153	if (is_p9) {
154		// Count pattern cache too
155		setup_event(&events[2], PM_BR_PRED_PCACHE,  "PM_BR_PRED_PCACHE");
156		setup_event(&events[3], PM_BR_MPRED_PCACHE, "PM_BR_MPRED_PCACHE");
157
158		FAIL_IF(event_open_with_group(&events[2], events[0].fd) == -1);
159		FAIL_IF(event_open_with_group(&events[3], events[0].fd) == -1);
160	}
161
162	FAIL_IF(do_count_loop(events, is_p9, &miss_percent));
163
164	event_report_justified(&events[0], 18, 10);
165	event_report_justified(&events[1], 18, 10);
166	event_close(&events[0]);
167	event_close(&events[1]);
168
169	if (is_p9) {
170		event_report_justified(&events[2], 18, 10);
171		event_report_justified(&events[3], 18, 10);
172		event_close(&events[2]);
173		event_close(&events[3]);
174	}
175
176	printf("Miss percent %lld %%\n", miss_percent);
177
178	switch (state) {
179	case VULNERABLE:
180	case NOT_AFFECTED:
181	case COUNT_CACHE_FLUSH_SW:
182	case COUNT_CACHE_FLUSH_HW:
183		// These should all not affect userspace branch prediction
184		if (miss_percent > 15) {
185			if (miss_percent > 95) {
186				/*
187				 * Such a mismatch may be caused by a system being unaware
188				 * the count cache is disabled. This may be to enable
189				 * guest migration between hosts with different settings.
190				 * Return skip code to avoid detecting this as an error.
191				 * We are not vulnerable and reporting otherwise, so
192				 * missing such a mismatch is safe.
193				 */
194				printf("Branch misses > 95%% unexpected in this configuration.\n");
195				printf("Count cache likely disabled without Linux knowing.\n");
196				if (state == COUNT_CACHE_FLUSH_SW)
197					printf("WARNING: Kernel performing unnecessary flushes.\n");
198				return 4;
199			}
200			printf("Branch misses > 15%% unexpected in this configuration!\n");
201			printf("Possible mismatch between reported & actual mitigation\n");
202
203			return 1;
204		}
205		break;
206	case BRANCH_SERIALISATION:
207		// This seems to affect userspace branch prediction a bit?
208		if (miss_percent > 25) {
209			printf("Branch misses > 25%% unexpected in this configuration!\n");
210			printf("Possible mismatch between reported & actual mitigation\n");
211			return 1;
212		}
213		break;
214	case COUNT_CACHE_DISABLED:
215		if (miss_percent < 95) {
216			printf("Branch misses < 95%% unexpected in this configuration!\n");
217			printf("Possible mismatch between reported & actual mitigation\n");
218			return 1;
219		}
220		break;
221	case UNKNOWN:
222	case BTB_FLUSH:
223		printf("Not sure!\n");
224		return 1;
225	}
226
227	printf("OK - Measured branch prediction rates match reported spectre v2 mitigation.\n");
228
229	return 0;
230}
231
232int main(int argc, char *argv[])
233{
234	return test_harness(spectre_v2_test, "spectre_v2");
235}
236