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