| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- // 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
- 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) {
- if (miss_percent > 95) {
- /*
- * Such a mismatch may be caused by a system being unaware
- * the count cache is disabled. This may be to enable
- * guest migration between hosts with different settings.
- * Return skip code to avoid detecting this as an error.
- * We are not vulnerable and reporting otherwise, so
- * missing such a mismatch is safe.
- */
- printf("Branch misses > 95%% unexpected in this configuration.\n");
- printf("Count cache likely disabled without Linux knowing.\n");
- if (state == COUNT_CACHE_FLUSH_SW)
- printf("WARNING: Kernel performing unnecessary flushes.\n");
- return 4;
- }
- printf("Branch misses > 15%% unexpected in this configuration!\n");
- printf("Possible mismatch between reported & actual mitigation\n");
- 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 mismatch between reported & actual mitigation\n");
- return 1;
- }
- break;
- case COUNT_CACHE_DISABLED:
- if (miss_percent < 95) {
- printf("Branch misses < 95%% unexpected in this configuration!\n");
- printf("Possible mismatch 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");
- }
|