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