// SPDX-License-Identifier: GPL-2.0 #include #include #include "util/kvm-stat.h" #include "util/parse-events.h" #include "util/debug.h" #include "util/evsel.h" #include "util/evlist.h" #include "util/pmus.h" #define LOONGARCH_EXCEPTION_INT 0 #define LOONGARCH_EXCEPTION_PIL 1 #define LOONGARCH_EXCEPTION_PIS 2 #define LOONGARCH_EXCEPTION_PIF 3 #define LOONGARCH_EXCEPTION_PME 4 #define LOONGARCH_EXCEPTION_FPD 15 #define LOONGARCH_EXCEPTION_SXD 16 #define LOONGARCH_EXCEPTION_ASXD 17 #define LOONGARCH_EXCEPTION_GSPR 22 #define LOONGARCH_EXCEPTION_CPUCFG 100 #define LOONGARCH_EXCEPTION_CSR 101 #define LOONGARCH_EXCEPTION_IOCSR 102 #define LOONGARCH_EXCEPTION_IDLE 103 #define LOONGARCH_EXCEPTION_OTHERS 104 #define LOONGARCH_EXCEPTION_HVC 23 #define loongarch_exception_type \ {LOONGARCH_EXCEPTION_INT, "Interrupt" }, \ {LOONGARCH_EXCEPTION_PIL, "Mem Read" }, \ {LOONGARCH_EXCEPTION_PIS, "Mem Store" }, \ {LOONGARCH_EXCEPTION_PIF, "Inst Fetch" }, \ {LOONGARCH_EXCEPTION_PME, "Mem Modify" }, \ {LOONGARCH_EXCEPTION_FPD, "FPU" }, \ {LOONGARCH_EXCEPTION_SXD, "LSX" }, \ {LOONGARCH_EXCEPTION_ASXD, "LASX" }, \ {LOONGARCH_EXCEPTION_GSPR, "Privilege Error" }, \ {LOONGARCH_EXCEPTION_HVC, "Hypercall" }, \ {LOONGARCH_EXCEPTION_CPUCFG, "CPUCFG" }, \ {LOONGARCH_EXCEPTION_CSR, "CSR" }, \ {LOONGARCH_EXCEPTION_IOCSR, "IOCSR" }, \ {LOONGARCH_EXCEPTION_IDLE, "Idle" }, \ {LOONGARCH_EXCEPTION_OTHERS, "Others" } define_exit_reasons_table(loongarch_exit_reasons, loongarch_exception_type); const char *vcpu_id_str = "vcpu_id"; const char *kvm_exit_reason = "reason"; const char *kvm_entry_trace = "kvm:kvm_enter"; const char *kvm_reenter_trace = "kvm:kvm_reenter"; const char *kvm_exit_trace = "kvm:kvm_exit"; const char *kvm_events_tp[] = { "kvm:kvm_enter", "kvm:kvm_reenter", "kvm:kvm_exit", "kvm:kvm_exit_gspr", NULL, }; static bool event_begin(struct evsel *evsel, struct perf_sample *sample, struct event_key *key) { return exit_event_begin(evsel, sample, key); } static bool event_end(struct evsel *evsel, struct perf_sample *sample __maybe_unused, struct event_key *key __maybe_unused) { /* * LoongArch kvm is different with other architectures * * There is kvm:kvm_reenter or kvm:kvm_enter event adjacent with * kvm:kvm_exit event. * kvm:kvm_enter means returning to vmm and then to guest * kvm:kvm_reenter means returning to guest immediately */ return evsel__name_is(evsel, kvm_entry_trace) || evsel__name_is(evsel, kvm_reenter_trace); } static void event_gspr_get_key(struct evsel *evsel, struct perf_sample *sample, struct event_key *key) { unsigned int insn; key->key = LOONGARCH_EXCEPTION_OTHERS; insn = evsel__intval(evsel, sample, "inst_word"); switch (insn >> 24) { case 0: /* CPUCFG inst trap */ if ((insn >> 10) == 0x1b) key->key = LOONGARCH_EXCEPTION_CPUCFG; break; case 4: /* CSR inst trap */ key->key = LOONGARCH_EXCEPTION_CSR; break; case 6: /* IOCSR inst trap */ if ((insn >> 15) == 0xc90) key->key = LOONGARCH_EXCEPTION_IOCSR; else if ((insn >> 15) == 0xc91) /* Idle inst trap */ key->key = LOONGARCH_EXCEPTION_IDLE; break; default: key->key = LOONGARCH_EXCEPTION_OTHERS; break; } } static struct child_event_ops child_events[] = { { .name = "kvm:kvm_exit_gspr", .get_key = event_gspr_get_key }, { NULL, NULL }, }; static struct kvm_events_ops exit_events = { .is_begin_event = event_begin, .is_end_event = event_end, .child_ops = child_events, .decode_key = exit_event_decode_key, .name = "VM-EXIT" }; struct kvm_reg_events_ops kvm_reg_events_ops[] = { { .name = "vmexit", .ops = &exit_events, }, { NULL, NULL }, }; const char * const kvm_skip_events[] = { NULL, }; int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused) { kvm->exit_reasons_isa = "loongarch64"; kvm->exit_reasons = loongarch_exit_reasons; return 0; }