cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

spectre_v2.c (6214B)


      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}