18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci * Copyright 2018-2019 IBM Corporation.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#define __SANE_USERSPACE_TYPES__
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <sys/types.h>
108c2ecf20Sopenharmony_ci#include <stdint.h>
118c2ecf20Sopenharmony_ci#include <malloc.h>
128c2ecf20Sopenharmony_ci#include <unistd.h>
138c2ecf20Sopenharmony_ci#include <stdlib.h>
148c2ecf20Sopenharmony_ci#include <string.h>
158c2ecf20Sopenharmony_ci#include <stdio.h>
168c2ecf20Sopenharmony_ci#include <sys/prctl.h>
178c2ecf20Sopenharmony_ci#include "utils.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "../pmu/event.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciextern void pattern_cache_loop(void);
238c2ecf20Sopenharmony_ciextern void indirect_branch_loop(void);
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int do_count_loop(struct event *events, bool is_p9, s64 *miss_percent)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	u64 pred, mpred;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	prctl(PR_TASK_PERF_EVENTS_ENABLE);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	if (is_p9)
328c2ecf20Sopenharmony_ci		pattern_cache_loop();
338c2ecf20Sopenharmony_ci	else
348c2ecf20Sopenharmony_ci		indirect_branch_loop();
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	prctl(PR_TASK_PERF_EVENTS_DISABLE);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	event_read(&events[0]);
398c2ecf20Sopenharmony_ci	event_read(&events[1]);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	// We could scale all the events by running/enabled but we're lazy
428c2ecf20Sopenharmony_ci	// As long as the PMU is uncontended they should all run
438c2ecf20Sopenharmony_ci	FAIL_IF(events[0].result.running != events[0].result.enabled);
448c2ecf20Sopenharmony_ci	FAIL_IF(events[1].result.running != events[1].result.enabled);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	pred =  events[0].result.value;
478c2ecf20Sopenharmony_ci	mpred = events[1].result.value;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (is_p9) {
508c2ecf20Sopenharmony_ci		event_read(&events[2]);
518c2ecf20Sopenharmony_ci		event_read(&events[3]);
528c2ecf20Sopenharmony_ci		FAIL_IF(events[2].result.running != events[2].result.enabled);
538c2ecf20Sopenharmony_ci		FAIL_IF(events[3].result.running != events[3].result.enabled);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		pred  += events[2].result.value;
568c2ecf20Sopenharmony_ci		mpred += events[3].result.value;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	*miss_percent = 100 * mpred / pred;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return 0;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void setup_event(struct event *e, u64 config, char *name)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	event_init_named(e, config, name);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	e->attr.disabled = 1;
698c2ecf20Sopenharmony_ci	e->attr.exclude_kernel = 1;
708c2ecf20Sopenharmony_ci	e->attr.exclude_hv = 1;
718c2ecf20Sopenharmony_ci	e->attr.exclude_idle = 1;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cienum spectre_v2_state {
758c2ecf20Sopenharmony_ci	VULNERABLE = 0,
768c2ecf20Sopenharmony_ci	UNKNOWN = 1,		// Works with FAIL_IF()
778c2ecf20Sopenharmony_ci	NOT_AFFECTED,
788c2ecf20Sopenharmony_ci	BRANCH_SERIALISATION,
798c2ecf20Sopenharmony_ci	COUNT_CACHE_DISABLED,
808c2ecf20Sopenharmony_ci	COUNT_CACHE_FLUSH_SW,
818c2ecf20Sopenharmony_ci	COUNT_CACHE_FLUSH_HW,
828c2ecf20Sopenharmony_ci	BTB_FLUSH,
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic enum spectre_v2_state get_sysfs_state(void)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	enum spectre_v2_state state = UNKNOWN;
888c2ecf20Sopenharmony_ci	char buf[256];
898c2ecf20Sopenharmony_ci	int len;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	memset(buf, 0, sizeof(buf));
928c2ecf20Sopenharmony_ci	FAIL_IF(read_sysfs_file("devices/system/cpu/vulnerabilities/spectre_v2", buf, sizeof(buf)));
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	// Make sure it's NULL terminated
958c2ecf20Sopenharmony_ci	buf[sizeof(buf) - 1] = '\0';
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	// Trim the trailing newline
988c2ecf20Sopenharmony_ci	len = strlen(buf);
998c2ecf20Sopenharmony_ci	FAIL_IF(len < 1);
1008c2ecf20Sopenharmony_ci	buf[len - 1] = '\0';
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	printf("sysfs reports: '%s'\n", buf);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	// Order matters
1058c2ecf20Sopenharmony_ci	if (strstr(buf, "Vulnerable"))
1068c2ecf20Sopenharmony_ci		state = VULNERABLE;
1078c2ecf20Sopenharmony_ci	else if (strstr(buf, "Not affected"))
1088c2ecf20Sopenharmony_ci		state = NOT_AFFECTED;
1098c2ecf20Sopenharmony_ci	else if (strstr(buf, "Indirect branch serialisation (kernel only)"))
1108c2ecf20Sopenharmony_ci		state = BRANCH_SERIALISATION;
1118c2ecf20Sopenharmony_ci	else if (strstr(buf, "Indirect branch cache disabled"))
1128c2ecf20Sopenharmony_ci		state = COUNT_CACHE_DISABLED;
1138c2ecf20Sopenharmony_ci	else if (strstr(buf, "Software count cache flush (hardware accelerated)"))
1148c2ecf20Sopenharmony_ci		state = COUNT_CACHE_FLUSH_HW;
1158c2ecf20Sopenharmony_ci	else if (strstr(buf, "Software count cache flush"))
1168c2ecf20Sopenharmony_ci		state = COUNT_CACHE_FLUSH_SW;
1178c2ecf20Sopenharmony_ci	else if (strstr(buf, "Branch predictor state flush"))
1188c2ecf20Sopenharmony_ci		state = BTB_FLUSH;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return state;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define PM_BR_PRED_CCACHE	0x040a4	// P8 + P9
1248c2ecf20Sopenharmony_ci#define PM_BR_MPRED_CCACHE	0x040ac	// P8 + P9
1258c2ecf20Sopenharmony_ci#define PM_BR_PRED_PCACHE	0x048a0	// P9 only
1268c2ecf20Sopenharmony_ci#define PM_BR_MPRED_PCACHE	0x048b0	// P9 only
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci#define SPRN_PVR 287
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ciint spectre_v2_test(void)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	enum spectre_v2_state state;
1338c2ecf20Sopenharmony_ci	struct event events[4];
1348c2ecf20Sopenharmony_ci	s64 miss_percent;
1358c2ecf20Sopenharmony_ci	bool is_p9;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	// The PMU events we use only work on Power8 or later
1388c2ecf20Sopenharmony_ci	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	state = get_sysfs_state();
1418c2ecf20Sopenharmony_ci	if (state == UNKNOWN) {
1428c2ecf20Sopenharmony_ci		printf("Error: couldn't determine spectre_v2 mitigation state?\n");
1438c2ecf20Sopenharmony_ci		return -1;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	memset(events, 0, sizeof(events));
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	setup_event(&events[0], PM_BR_PRED_CCACHE,  "PM_BR_PRED_CCACHE");
1498c2ecf20Sopenharmony_ci	setup_event(&events[1], PM_BR_MPRED_CCACHE, "PM_BR_MPRED_CCACHE");
1508c2ecf20Sopenharmony_ci	FAIL_IF(event_open(&events[0]));
1518c2ecf20Sopenharmony_ci	FAIL_IF(event_open_with_group(&events[1], events[0].fd) == -1);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	is_p9 = ((mfspr(SPRN_PVR) >>  16) & 0xFFFF) == 0x4e;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (is_p9) {
1568c2ecf20Sopenharmony_ci		// Count pattern cache too
1578c2ecf20Sopenharmony_ci		setup_event(&events[2], PM_BR_PRED_PCACHE,  "PM_BR_PRED_PCACHE");
1588c2ecf20Sopenharmony_ci		setup_event(&events[3], PM_BR_MPRED_PCACHE, "PM_BR_MPRED_PCACHE");
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci		FAIL_IF(event_open_with_group(&events[2], events[0].fd) == -1);
1618c2ecf20Sopenharmony_ci		FAIL_IF(event_open_with_group(&events[3], events[0].fd) == -1);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	FAIL_IF(do_count_loop(events, is_p9, &miss_percent));
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	event_report_justified(&events[0], 18, 10);
1678c2ecf20Sopenharmony_ci	event_report_justified(&events[1], 18, 10);
1688c2ecf20Sopenharmony_ci	event_close(&events[0]);
1698c2ecf20Sopenharmony_ci	event_close(&events[1]);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (is_p9) {
1728c2ecf20Sopenharmony_ci		event_report_justified(&events[2], 18, 10);
1738c2ecf20Sopenharmony_ci		event_report_justified(&events[3], 18, 10);
1748c2ecf20Sopenharmony_ci		event_close(&events[2]);
1758c2ecf20Sopenharmony_ci		event_close(&events[3]);
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	printf("Miss percent %lld %%\n", miss_percent);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	switch (state) {
1818c2ecf20Sopenharmony_ci	case VULNERABLE:
1828c2ecf20Sopenharmony_ci	case NOT_AFFECTED:
1838c2ecf20Sopenharmony_ci	case COUNT_CACHE_FLUSH_SW:
1848c2ecf20Sopenharmony_ci	case COUNT_CACHE_FLUSH_HW:
1858c2ecf20Sopenharmony_ci		// These should all not affect userspace branch prediction
1868c2ecf20Sopenharmony_ci		if (miss_percent > 15) {
1878c2ecf20Sopenharmony_ci			printf("Branch misses > 15%% unexpected in this configuration!\n");
1888c2ecf20Sopenharmony_ci			printf("Possible mis-match between reported & actual mitigation\n");
1898c2ecf20Sopenharmony_ci			/*
1908c2ecf20Sopenharmony_ci			 * Such a mismatch may be caused by a guest system
1918c2ecf20Sopenharmony_ci			 * reporting as vulnerable when the host is mitigated.
1928c2ecf20Sopenharmony_ci			 * Return skip code to avoid detecting this as an error.
1938c2ecf20Sopenharmony_ci			 * We are not vulnerable and reporting otherwise, so
1948c2ecf20Sopenharmony_ci			 * missing such a mismatch is safe.
1958c2ecf20Sopenharmony_ci			 */
1968c2ecf20Sopenharmony_ci			if (miss_percent > 95)
1978c2ecf20Sopenharmony_ci				return 4;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci			return 1;
2008c2ecf20Sopenharmony_ci		}
2018c2ecf20Sopenharmony_ci		break;
2028c2ecf20Sopenharmony_ci	case BRANCH_SERIALISATION:
2038c2ecf20Sopenharmony_ci		// This seems to affect userspace branch prediction a bit?
2048c2ecf20Sopenharmony_ci		if (miss_percent > 25) {
2058c2ecf20Sopenharmony_ci			printf("Branch misses > 25%% unexpected in this configuration!\n");
2068c2ecf20Sopenharmony_ci			printf("Possible mis-match between reported & actual mitigation\n");
2078c2ecf20Sopenharmony_ci			return 1;
2088c2ecf20Sopenharmony_ci		}
2098c2ecf20Sopenharmony_ci		break;
2108c2ecf20Sopenharmony_ci	case COUNT_CACHE_DISABLED:
2118c2ecf20Sopenharmony_ci		if (miss_percent < 95) {
2128c2ecf20Sopenharmony_ci			printf("Branch misses < 20%% unexpected in this configuration!\n");
2138c2ecf20Sopenharmony_ci			printf("Possible mis-match between reported & actual mitigation\n");
2148c2ecf20Sopenharmony_ci			return 1;
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci		break;
2178c2ecf20Sopenharmony_ci	case UNKNOWN:
2188c2ecf20Sopenharmony_ci	case BTB_FLUSH:
2198c2ecf20Sopenharmony_ci		printf("Not sure!\n");
2208c2ecf20Sopenharmony_ci		return 1;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	printf("OK - Measured branch prediction rates match reported spectre v2 mitigation.\n");
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return 0;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ciint main(int argc, char *argv[])
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	return test_harness(spectre_v2_test, "spectre_v2");
2318c2ecf20Sopenharmony_ci}
232