brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · 5b2abb7 Raw
236 lines · 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 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	else34		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 lazy42	// As long as the PMU is uncontended they should all run43	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 terminated95	buf[sizeof(buf) - 1] = '\0';96 97	// Trim the trailing newline98	len = strlen(buf);99	FAIL_IF(len < 1);100	buf[len - 1] = '\0';101 102	printf("sysfs reports: '%s'\n", buf);103 104	// Order matters105	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 + P9124#define PM_BR_MPRED_CCACHE	0x040ac	// P8 + P9125#define PM_BR_PRED_PCACHE	0x048a0	// P9 only126#define PM_BR_MPRED_PCACHE	0x048b0	// P9 only127 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 later136	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 too155		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 prediction184		if (miss_percent > 15) {185			if (miss_percent > 95) {186				/*187				 * Such a mismatch may be caused by a system being unaware188				 * the count cache is disabled. This may be to enable189				 * 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, so192				 * 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