A mis-match between reported and actual mitigation is not restricted to the Vulnerable case. The guest might also report the mitigation as "Software count cache flush" and the host will still mitigate with branch cache disabled. So, instead of skipping depending on the detected mitigation, simply skip whenever the detected miss_percent is the expected one for a fully mitigated system, that is, above 95%. Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20211207130557.40566-1-cascardo@canonical.com
232 lines
5.8 KiB
C
232 lines
5.8 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
/*
|
|
* Copyright 2018-2019 IBM Corporation.
|
|
*/
|
|
|
|
#define __SANE_USERSPACE_TYPES__
|
|
|
|
#include <sys/types.h>
|
|
#include <stdint.h>
|
|
#include <malloc.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <sys/prctl.h>
|
|
#include "utils.h"
|
|
|
|
#include "../pmu/event.h"
|
|
|
|
|
|
extern void pattern_cache_loop(void);
|
|
extern void indirect_branch_loop(void);
|
|
|
|
static int do_count_loop(struct event *events, bool is_p9, s64 *miss_percent)
|
|
{
|
|
u64 pred, mpred;
|
|
|
|
prctl(PR_TASK_PERF_EVENTS_ENABLE);
|
|
|
|
if (is_p9)
|
|
pattern_cache_loop();
|
|
else
|
|
indirect_branch_loop();
|
|
|
|
prctl(PR_TASK_PERF_EVENTS_DISABLE);
|
|
|
|
event_read(&events[0]);
|
|
event_read(&events[1]);
|
|
|
|
// We could scale all the events by running/enabled but we're lazy
|
|
// As long as the PMU is uncontended they should all run
|
|
FAIL_IF(events[0].result.running != events[0].result.enabled);
|
|
FAIL_IF(events[1].result.running != events[1].result.enabled);
|
|
|
|
pred = events[0].result.value;
|
|
mpred = events[1].result.value;
|
|
|
|
if (is_p9) {
|
|
event_read(&events[2]);
|
|
event_read(&events[3]);
|
|
FAIL_IF(events[2].result.running != events[2].result.enabled);
|
|
FAIL_IF(events[3].result.running != events[3].result.enabled);
|
|
|
|
pred += events[2].result.value;
|
|
mpred += events[3].result.value;
|
|
}
|
|
|
|
*miss_percent = 100 * mpred / pred;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void setup_event(struct event *e, u64 config, char *name)
|
|
{
|
|
event_init_named(e, config, name);
|
|
|
|
e->attr.disabled = 1;
|
|
e->attr.exclude_kernel = 1;
|
|
e->attr.exclude_hv = 1;
|
|
e->attr.exclude_idle = 1;
|
|
}
|
|
|
|
enum spectre_v2_state {
|
|
VULNERABLE = 0,
|
|
UNKNOWN = 1, // Works with FAIL_IF()
|
|
NOT_AFFECTED,
|
|
BRANCH_SERIALISATION,
|
|
COUNT_CACHE_DISABLED,
|
|
COUNT_CACHE_FLUSH_SW,
|
|
COUNT_CACHE_FLUSH_HW,
|
|
BTB_FLUSH,
|
|
};
|
|
|
|
static enum spectre_v2_state get_sysfs_state(void)
|
|
{
|
|
enum spectre_v2_state state = UNKNOWN;
|
|
char buf[256];
|
|
int len;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
FAIL_IF(read_sysfs_file("devices/system/cpu/vulnerabilities/spectre_v2", buf, sizeof(buf)));
|
|
|
|
// Make sure it's NULL terminated
|
|
buf[sizeof(buf) - 1] = '\0';
|
|
|
|
// Trim the trailing newline
|
|
len = strlen(buf);
|
|
FAIL_IF(len < 1);
|
|
buf[len - 1] = '\0';
|
|
|
|
printf("sysfs reports: '%s'\n", buf);
|
|
|
|
// Order matters
|
|
if (strstr(buf, "Vulnerable"))
|
|
state = VULNERABLE;
|
|
else if (strstr(buf, "Not affected"))
|
|
state = NOT_AFFECTED;
|
|
else if (strstr(buf, "Indirect branch serialisation (kernel only)"))
|
|
state = BRANCH_SERIALISATION;
|
|
else if (strstr(buf, "Indirect branch cache disabled"))
|
|
state = COUNT_CACHE_DISABLED;
|
|
else if (strstr(buf, "Software count cache flush (hardware accelerated)"))
|
|
state = COUNT_CACHE_FLUSH_HW;
|
|
else if (strstr(buf, "Software count cache flush"))
|
|
state = COUNT_CACHE_FLUSH_SW;
|
|
else if (strstr(buf, "Branch predictor state flush"))
|
|
state = BTB_FLUSH;
|
|
|
|
return state;
|
|
}
|
|
|
|
#define PM_BR_PRED_CCACHE 0x040a4 // P8 + P9
|
|
#define PM_BR_MPRED_CCACHE 0x040ac // P8 + P9
|
|
#define PM_BR_PRED_PCACHE 0x048a0 // P9 only
|
|
#define PM_BR_MPRED_PCACHE 0x048b0 // P9 only
|
|
|
|
#define SPRN_PVR 287
|
|
|
|
int spectre_v2_test(void)
|
|
{
|
|
enum spectre_v2_state state;
|
|
struct event events[4];
|
|
s64 miss_percent;
|
|
bool is_p9;
|
|
|
|
// The PMU events we use only work on Power8 or later
|
|
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
|
|
|
|
state = get_sysfs_state();
|
|
if (state == UNKNOWN) {
|
|
printf("Error: couldn't determine spectre_v2 mitigation state?\n");
|
|
return -1;
|
|
}
|
|
|
|
memset(events, 0, sizeof(events));
|
|
|
|
setup_event(&events[0], PM_BR_PRED_CCACHE, "PM_BR_PRED_CCACHE");
|
|
setup_event(&events[1], PM_BR_MPRED_CCACHE, "PM_BR_MPRED_CCACHE");
|
|
FAIL_IF(event_open(&events[0]));
|
|
FAIL_IF(event_open_with_group(&events[1], events[0].fd) == -1);
|
|
|
|
is_p9 = ((mfspr(SPRN_PVR) >> 16) & 0xFFFF) == 0x4e;
|
|
|
|
if (is_p9) {
|
|
// Count pattern cache too
|
|
setup_event(&events[2], PM_BR_PRED_PCACHE, "PM_BR_PRED_PCACHE");
|
|
setup_event(&events[3], PM_BR_MPRED_PCACHE, "PM_BR_MPRED_PCACHE");
|
|
|
|
FAIL_IF(event_open_with_group(&events[2], events[0].fd) == -1);
|
|
FAIL_IF(event_open_with_group(&events[3], events[0].fd) == -1);
|
|
}
|
|
|
|
FAIL_IF(do_count_loop(events, is_p9, &miss_percent));
|
|
|
|
event_report_justified(&events[0], 18, 10);
|
|
event_report_justified(&events[1], 18, 10);
|
|
event_close(&events[0]);
|
|
event_close(&events[1]);
|
|
|
|
if (is_p9) {
|
|
event_report_justified(&events[2], 18, 10);
|
|
event_report_justified(&events[3], 18, 10);
|
|
event_close(&events[2]);
|
|
event_close(&events[3]);
|
|
}
|
|
|
|
printf("Miss percent %lld %%\n", miss_percent);
|
|
|
|
switch (state) {
|
|
case VULNERABLE:
|
|
case NOT_AFFECTED:
|
|
case COUNT_CACHE_FLUSH_SW:
|
|
case COUNT_CACHE_FLUSH_HW:
|
|
// These should all not affect userspace branch prediction
|
|
if (miss_percent > 15) {
|
|
printf("Branch misses > 15%% unexpected in this configuration!\n");
|
|
printf("Possible mis-match between reported & actual mitigation\n");
|
|
/*
|
|
* Such a mismatch may be caused by a guest system
|
|
* reporting as vulnerable when the host is mitigated.
|
|
* Return skip code to avoid detecting this as an error.
|
|
* We are not vulnerable and reporting otherwise, so
|
|
* missing such a mismatch is safe.
|
|
*/
|
|
if (miss_percent > 95)
|
|
return 4;
|
|
|
|
return 1;
|
|
}
|
|
break;
|
|
case BRANCH_SERIALISATION:
|
|
// This seems to affect userspace branch prediction a bit?
|
|
if (miss_percent > 25) {
|
|
printf("Branch misses > 25%% unexpected in this configuration!\n");
|
|
printf("Possible mis-match between reported & actual mitigation\n");
|
|
return 1;
|
|
}
|
|
break;
|
|
case COUNT_CACHE_DISABLED:
|
|
if (miss_percent < 95) {
|
|
printf("Branch misses < 20%% unexpected in this configuration!\n");
|
|
printf("Possible mis-match between reported & actual mitigation\n");
|
|
return 1;
|
|
}
|
|
break;
|
|
case UNKNOWN:
|
|
case BTB_FLUSH:
|
|
printf("Not sure!\n");
|
|
return 1;
|
|
}
|
|
|
|
printf("OK - Measured branch prediction rates match reported spectre v2 mitigation.\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
return test_harness(spectre_v2_test, "spectre_v2");
|
|
}
|