162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Copyright 2018-2019 IBM Corporation.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define __SANE_USERSPACE_TYPES__
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <sys/types.h>
1062306a36Sopenharmony_ci#include <stdint.h>
1162306a36Sopenharmony_ci#include <malloc.h>
1262306a36Sopenharmony_ci#include <unistd.h>
1362306a36Sopenharmony_ci#include <stdlib.h>
1462306a36Sopenharmony_ci#include <string.h>
1562306a36Sopenharmony_ci#include <stdio.h>
1662306a36Sopenharmony_ci#include <sys/prctl.h>
1762306a36Sopenharmony_ci#include "utils.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "../pmu/event.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciextern void pattern_cache_loop(void);
2362306a36Sopenharmony_ciextern void indirect_branch_loop(void);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int do_count_loop(struct event *events, bool is_p9, s64 *miss_percent)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	u64 pred, mpred;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	prctl(PR_TASK_PERF_EVENTS_ENABLE);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (is_p9)
3262306a36Sopenharmony_ci		pattern_cache_loop();
3362306a36Sopenharmony_ci	else
3462306a36Sopenharmony_ci		indirect_branch_loop();
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	prctl(PR_TASK_PERF_EVENTS_DISABLE);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	event_read(&events[0]);
3962306a36Sopenharmony_ci	event_read(&events[1]);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	// We could scale all the events by running/enabled but we're lazy
4262306a36Sopenharmony_ci	// As long as the PMU is uncontended they should all run
4362306a36Sopenharmony_ci	FAIL_IF(events[0].result.running != events[0].result.enabled);
4462306a36Sopenharmony_ci	FAIL_IF(events[1].result.running != events[1].result.enabled);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	pred =  events[0].result.value;
4762306a36Sopenharmony_ci	mpred = events[1].result.value;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (is_p9) {
5062306a36Sopenharmony_ci		event_read(&events[2]);
5162306a36Sopenharmony_ci		event_read(&events[3]);
5262306a36Sopenharmony_ci		FAIL_IF(events[2].result.running != events[2].result.enabled);
5362306a36Sopenharmony_ci		FAIL_IF(events[3].result.running != events[3].result.enabled);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		pred  += events[2].result.value;
5662306a36Sopenharmony_ci		mpred += events[3].result.value;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	*miss_percent = 100 * mpred / pred;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return 0;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void setup_event(struct event *e, u64 config, char *name)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	event_init_named(e, config, name);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	e->attr.disabled = 1;
6962306a36Sopenharmony_ci	e->attr.exclude_kernel = 1;
7062306a36Sopenharmony_ci	e->attr.exclude_hv = 1;
7162306a36Sopenharmony_ci	e->attr.exclude_idle = 1;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cienum spectre_v2_state {
7562306a36Sopenharmony_ci	VULNERABLE = 0,
7662306a36Sopenharmony_ci	UNKNOWN = 1,		// Works with FAIL_IF()
7762306a36Sopenharmony_ci	NOT_AFFECTED,
7862306a36Sopenharmony_ci	BRANCH_SERIALISATION,
7962306a36Sopenharmony_ci	COUNT_CACHE_DISABLED,
8062306a36Sopenharmony_ci	COUNT_CACHE_FLUSH_SW,
8162306a36Sopenharmony_ci	COUNT_CACHE_FLUSH_HW,
8262306a36Sopenharmony_ci	BTB_FLUSH,
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic enum spectre_v2_state get_sysfs_state(void)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	enum spectre_v2_state state = UNKNOWN;
8862306a36Sopenharmony_ci	char buf[256];
8962306a36Sopenharmony_ci	int len;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	memset(buf, 0, sizeof(buf));
9262306a36Sopenharmony_ci	FAIL_IF(read_sysfs_file("devices/system/cpu/vulnerabilities/spectre_v2", buf, sizeof(buf)));
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	// Make sure it's NULL terminated
9562306a36Sopenharmony_ci	buf[sizeof(buf) - 1] = '\0';
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	// Trim the trailing newline
9862306a36Sopenharmony_ci	len = strlen(buf);
9962306a36Sopenharmony_ci	FAIL_IF(len < 1);
10062306a36Sopenharmony_ci	buf[len - 1] = '\0';
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	printf("sysfs reports: '%s'\n", buf);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	// Order matters
10562306a36Sopenharmony_ci	if (strstr(buf, "Vulnerable"))
10662306a36Sopenharmony_ci		state = VULNERABLE;
10762306a36Sopenharmony_ci	else if (strstr(buf, "Not affected"))
10862306a36Sopenharmony_ci		state = NOT_AFFECTED;
10962306a36Sopenharmony_ci	else if (strstr(buf, "Indirect branch serialisation (kernel only)"))
11062306a36Sopenharmony_ci		state = BRANCH_SERIALISATION;
11162306a36Sopenharmony_ci	else if (strstr(buf, "Indirect branch cache disabled"))
11262306a36Sopenharmony_ci		state = COUNT_CACHE_DISABLED;
11362306a36Sopenharmony_ci	else if (strstr(buf, "Software count cache flush (hardware accelerated)"))
11462306a36Sopenharmony_ci		state = COUNT_CACHE_FLUSH_HW;
11562306a36Sopenharmony_ci	else if (strstr(buf, "Software count cache flush"))
11662306a36Sopenharmony_ci		state = COUNT_CACHE_FLUSH_SW;
11762306a36Sopenharmony_ci	else if (strstr(buf, "Branch predictor state flush"))
11862306a36Sopenharmony_ci		state = BTB_FLUSH;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return state;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define PM_BR_PRED_CCACHE	0x040a4	// P8 + P9
12462306a36Sopenharmony_ci#define PM_BR_MPRED_CCACHE	0x040ac	// P8 + P9
12562306a36Sopenharmony_ci#define PM_BR_PRED_PCACHE	0x048a0	// P9 only
12662306a36Sopenharmony_ci#define PM_BR_MPRED_PCACHE	0x048b0	// P9 only
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ciint spectre_v2_test(void)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	enum spectre_v2_state state;
13162306a36Sopenharmony_ci	struct event events[4];
13262306a36Sopenharmony_ci	s64 miss_percent;
13362306a36Sopenharmony_ci	bool is_p9;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	// The PMU events we use only work on Power8 or later
13662306a36Sopenharmony_ci	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	state = get_sysfs_state();
13962306a36Sopenharmony_ci	if (state == UNKNOWN) {
14062306a36Sopenharmony_ci		printf("Error: couldn't determine spectre_v2 mitigation state?\n");
14162306a36Sopenharmony_ci		return -1;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	memset(events, 0, sizeof(events));
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	setup_event(&events[0], PM_BR_PRED_CCACHE,  "PM_BR_PRED_CCACHE");
14762306a36Sopenharmony_ci	setup_event(&events[1], PM_BR_MPRED_CCACHE, "PM_BR_MPRED_CCACHE");
14862306a36Sopenharmony_ci	FAIL_IF(event_open(&events[0]));
14962306a36Sopenharmony_ci	FAIL_IF(event_open_with_group(&events[1], events[0].fd) == -1);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	is_p9 = ((mfspr(SPRN_PVR) >>  16) & 0xFFFF) == 0x4e;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (is_p9) {
15462306a36Sopenharmony_ci		// Count pattern cache too
15562306a36Sopenharmony_ci		setup_event(&events[2], PM_BR_PRED_PCACHE,  "PM_BR_PRED_PCACHE");
15662306a36Sopenharmony_ci		setup_event(&events[3], PM_BR_MPRED_PCACHE, "PM_BR_MPRED_PCACHE");
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		FAIL_IF(event_open_with_group(&events[2], events[0].fd) == -1);
15962306a36Sopenharmony_ci		FAIL_IF(event_open_with_group(&events[3], events[0].fd) == -1);
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	FAIL_IF(do_count_loop(events, is_p9, &miss_percent));
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	event_report_justified(&events[0], 18, 10);
16562306a36Sopenharmony_ci	event_report_justified(&events[1], 18, 10);
16662306a36Sopenharmony_ci	event_close(&events[0]);
16762306a36Sopenharmony_ci	event_close(&events[1]);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (is_p9) {
17062306a36Sopenharmony_ci		event_report_justified(&events[2], 18, 10);
17162306a36Sopenharmony_ci		event_report_justified(&events[3], 18, 10);
17262306a36Sopenharmony_ci		event_close(&events[2]);
17362306a36Sopenharmony_ci		event_close(&events[3]);
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	printf("Miss percent %lld %%\n", miss_percent);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	switch (state) {
17962306a36Sopenharmony_ci	case VULNERABLE:
18062306a36Sopenharmony_ci	case NOT_AFFECTED:
18162306a36Sopenharmony_ci	case COUNT_CACHE_FLUSH_SW:
18262306a36Sopenharmony_ci	case COUNT_CACHE_FLUSH_HW:
18362306a36Sopenharmony_ci		// These should all not affect userspace branch prediction
18462306a36Sopenharmony_ci		if (miss_percent > 15) {
18562306a36Sopenharmony_ci			if (miss_percent > 95) {
18662306a36Sopenharmony_ci				/*
18762306a36Sopenharmony_ci				 * Such a mismatch may be caused by a system being unaware
18862306a36Sopenharmony_ci				 * the count cache is disabled. This may be to enable
18962306a36Sopenharmony_ci				 * guest migration between hosts with different settings.
19062306a36Sopenharmony_ci				 * Return skip code to avoid detecting this as an error.
19162306a36Sopenharmony_ci				 * We are not vulnerable and reporting otherwise, so
19262306a36Sopenharmony_ci				 * missing such a mismatch is safe.
19362306a36Sopenharmony_ci				 */
19462306a36Sopenharmony_ci				printf("Branch misses > 95%% unexpected in this configuration.\n");
19562306a36Sopenharmony_ci				printf("Count cache likely disabled without Linux knowing.\n");
19662306a36Sopenharmony_ci				if (state == COUNT_CACHE_FLUSH_SW)
19762306a36Sopenharmony_ci					printf("WARNING: Kernel performing unnecessary flushes.\n");
19862306a36Sopenharmony_ci				return 4;
19962306a36Sopenharmony_ci			}
20062306a36Sopenharmony_ci			printf("Branch misses > 15%% unexpected in this configuration!\n");
20162306a36Sopenharmony_ci			printf("Possible mismatch between reported & actual mitigation\n");
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci			return 1;
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci		break;
20662306a36Sopenharmony_ci	case BRANCH_SERIALISATION:
20762306a36Sopenharmony_ci		// This seems to affect userspace branch prediction a bit?
20862306a36Sopenharmony_ci		if (miss_percent > 25) {
20962306a36Sopenharmony_ci			printf("Branch misses > 25%% unexpected in this configuration!\n");
21062306a36Sopenharmony_ci			printf("Possible mismatch between reported & actual mitigation\n");
21162306a36Sopenharmony_ci			return 1;
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci		break;
21462306a36Sopenharmony_ci	case COUNT_CACHE_DISABLED:
21562306a36Sopenharmony_ci		if (miss_percent < 95) {
21662306a36Sopenharmony_ci			printf("Branch misses < 95%% unexpected in this configuration!\n");
21762306a36Sopenharmony_ci			printf("Possible mismatch between reported & actual mitigation\n");
21862306a36Sopenharmony_ci			return 1;
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci		break;
22162306a36Sopenharmony_ci	case UNKNOWN:
22262306a36Sopenharmony_ci	case BTB_FLUSH:
22362306a36Sopenharmony_ci		printf("Not sure!\n");
22462306a36Sopenharmony_ci		return 1;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	printf("OK - Measured branch prediction rates match reported spectre v2 mitigation.\n");
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return 0;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ciint main(int argc, char *argv[])
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	return test_harness(spectre_v2_test, "spectre_v2");
23562306a36Sopenharmony_ci}
236