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

kvm-stat.c (5177B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <errno.h>
      3#include "util/kvm-stat.h"
      4#include "util/parse-events.h"
      5#include "util/debug.h"
      6#include "util/evsel.h"
      7#include "util/evlist.h"
      8#include "util/pmu.h"
      9
     10#include "book3s_hv_exits.h"
     11#include "book3s_hcalls.h"
     12#include <subcmd/parse-options.h>
     13
     14#define NR_TPS 4
     15
     16const char *vcpu_id_str = "vcpu_id";
     17const int decode_str_len = 40;
     18const char *kvm_entry_trace = "kvm_hv:kvm_guest_enter";
     19const char *kvm_exit_trace = "kvm_hv:kvm_guest_exit";
     20
     21define_exit_reasons_table(hv_exit_reasons, kvm_trace_symbol_exit);
     22define_exit_reasons_table(hcall_reasons, kvm_trace_symbol_hcall);
     23
     24/* Tracepoints specific to ppc_book3s_hv */
     25const char *ppc_book3s_hv_kvm_tp[] = {
     26	"kvm_hv:kvm_guest_enter",
     27	"kvm_hv:kvm_guest_exit",
     28	"kvm_hv:kvm_hcall_enter",
     29	"kvm_hv:kvm_hcall_exit",
     30	NULL,
     31};
     32
     33/* 1 extra placeholder for NULL */
     34const char *kvm_events_tp[NR_TPS + 1];
     35const char *kvm_exit_reason;
     36
     37static void hcall_event_get_key(struct evsel *evsel,
     38				struct perf_sample *sample,
     39				struct event_key *key)
     40{
     41	key->info = 0;
     42	key->key = evsel__intval(evsel, sample, "req");
     43}
     44
     45static const char *get_hcall_exit_reason(u64 exit_code)
     46{
     47	struct exit_reasons_table *tbl = hcall_reasons;
     48
     49	while (tbl->reason != NULL) {
     50		if (tbl->exit_code == exit_code)
     51			return tbl->reason;
     52		tbl++;
     53	}
     54
     55	pr_debug("Unknown hcall code: %lld\n",
     56	       (unsigned long long)exit_code);
     57	return "UNKNOWN";
     58}
     59
     60static bool hcall_event_end(struct evsel *evsel,
     61			    struct perf_sample *sample __maybe_unused,
     62			    struct event_key *key __maybe_unused)
     63{
     64	return (!strcmp(evsel->name, kvm_events_tp[3]));
     65}
     66
     67static bool hcall_event_begin(struct evsel *evsel,
     68			      struct perf_sample *sample, struct event_key *key)
     69{
     70	if (!strcmp(evsel->name, kvm_events_tp[2])) {
     71		hcall_event_get_key(evsel, sample, key);
     72		return true;
     73	}
     74
     75	return false;
     76}
     77static void hcall_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
     78				   struct event_key *key,
     79				   char *decode)
     80{
     81	const char *hcall_reason = get_hcall_exit_reason(key->key);
     82
     83	scnprintf(decode, decode_str_len, "%s", hcall_reason);
     84}
     85
     86static struct kvm_events_ops hcall_events = {
     87	.is_begin_event = hcall_event_begin,
     88	.is_end_event = hcall_event_end,
     89	.decode_key = hcall_event_decode_key,
     90	.name = "HCALL-EVENT",
     91};
     92
     93static struct kvm_events_ops exit_events = {
     94	.is_begin_event = exit_event_begin,
     95	.is_end_event = exit_event_end,
     96	.decode_key = exit_event_decode_key,
     97	.name = "VM-EXIT"
     98};
     99
    100struct kvm_reg_events_ops kvm_reg_events_ops[] = {
    101	{ .name = "vmexit", .ops = &exit_events },
    102	{ .name = "hcall", .ops = &hcall_events },
    103	{ NULL, NULL },
    104};
    105
    106const char * const kvm_skip_events[] = {
    107	NULL,
    108};
    109
    110
    111static int is_tracepoint_available(const char *str, struct evlist *evlist)
    112{
    113	struct parse_events_error err;
    114	int ret;
    115
    116	parse_events_error__init(&err);
    117	ret = parse_events(evlist, str, &err);
    118	if (err.str)
    119		parse_events_error__print(&err, "tracepoint");
    120	parse_events_error__exit(&err);
    121	return ret;
    122}
    123
    124static int ppc__setup_book3s_hv(struct perf_kvm_stat *kvm,
    125				struct evlist *evlist)
    126{
    127	const char **events_ptr;
    128	int i, nr_tp = 0, err = -1;
    129
    130	/* Check for book3s_hv tracepoints */
    131	for (events_ptr = ppc_book3s_hv_kvm_tp; *events_ptr; events_ptr++) {
    132		err = is_tracepoint_available(*events_ptr, evlist);
    133		if (err)
    134			return -1;
    135		nr_tp++;
    136	}
    137
    138	for (i = 0; i < nr_tp; i++)
    139		kvm_events_tp[i] = ppc_book3s_hv_kvm_tp[i];
    140
    141	kvm_events_tp[i] = NULL;
    142	kvm_exit_reason = "trap";
    143	kvm->exit_reasons = hv_exit_reasons;
    144	kvm->exit_reasons_isa = "HV";
    145
    146	return 0;
    147}
    148
    149/* Wrapper to setup kvm tracepoints */
    150static int ppc__setup_kvm_tp(struct perf_kvm_stat *kvm)
    151{
    152	struct evlist *evlist = evlist__new();
    153
    154	if (evlist == NULL)
    155		return -ENOMEM;
    156
    157	/* Right now, only supported on book3s_hv */
    158	return ppc__setup_book3s_hv(kvm, evlist);
    159}
    160
    161int setup_kvm_events_tp(struct perf_kvm_stat *kvm)
    162{
    163	return ppc__setup_kvm_tp(kvm);
    164}
    165
    166int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused)
    167{
    168	int ret;
    169
    170	ret = ppc__setup_kvm_tp(kvm);
    171	if (ret) {
    172		kvm->exit_reasons = NULL;
    173		kvm->exit_reasons_isa = NULL;
    174	}
    175
    176	return ret;
    177}
    178
    179/*
    180 * In case of powerpc architecture, pmu registers are programmable
    181 * by guest kernel. So monitoring guest via host may not provide
    182 * valid samples with default 'cycles' event. It is better to use
    183 * 'trace_imc/trace_cycles' event for guest profiling, since it
    184 * can track the guest instruction pointer in the trace-record.
    185 *
    186 * Function to parse the arguments and return appropriate values.
    187 */
    188int kvm_add_default_arch_event(int *argc, const char **argv)
    189{
    190	const char **tmp;
    191	bool event = false;
    192	int i, j = *argc;
    193
    194	const struct option event_options[] = {
    195		OPT_BOOLEAN('e', "event", &event, NULL),
    196		OPT_END()
    197	};
    198
    199	tmp = calloc(j + 1, sizeof(char *));
    200	if (!tmp)
    201		return -EINVAL;
    202
    203	for (i = 0; i < j; i++)
    204		tmp[i] = argv[i];
    205
    206	parse_options(j, tmp, event_options, NULL, PARSE_OPT_KEEP_UNKNOWN);
    207	if (!event) {
    208		if (pmu_have_event("trace_imc", "trace_cycles")) {
    209			argv[j++] = strdup("-e");
    210			argv[j++] = strdup("trace_imc/trace_cycles/");
    211			*argc += 2;
    212		} else {
    213			free(tmp);
    214			return -EINVAL;
    215		}
    216	}
    217
    218	free(tmp);
    219	return 0;
    220}