1 // SPDX-License-Identifier: GPL-2.0 2 #include <errno.h> 3 #include <memory.h> 4 #include "util/kvm-stat.h" 5 #include "util/parse-events.h" 6 #include "util/debug.h" 7 #include "util/evsel.h" 8 #include "util/evlist.h" 9 #include "util/pmus.h" 10 11 #define LOONGARCH_EXCEPTION_INT 0 12 #define LOONGARCH_EXCEPTION_PIL 1 13 #define LOONGARCH_EXCEPTION_PIS 2 14 #define LOONGARCH_EXCEPTION_PIF 3 15 #define LOONGARCH_EXCEPTION_PME 4 16 #define LOONGARCH_EXCEPTION_FPD 15 17 #define LOONGARCH_EXCEPTION_SXD 16 18 #define LOONGARCH_EXCEPTION_ASXD 17 19 #define LOONGARCH_EXCEPTION_GSPR 22 20 #define LOONGARCH_EXCEPTION_CPUCFG 100 21 #define LOONGARCH_EXCEPTION_CSR 101 22 #define LOONGARCH_EXCEPTION_IOCSR 102 23 #define LOONGARCH_EXCEPTION_IDLE 103 24 #define LOONGARCH_EXCEPTION_OTHERS 104 25 #define LOONGARCH_EXCEPTION_HVC 23 26 27 #define loongarch_exception_type \ 28 {LOONGARCH_EXCEPTION_INT, "Interrupt" }, \ 29 {LOONGARCH_EXCEPTION_PIL, "Mem Read" }, \ 30 {LOONGARCH_EXCEPTION_PIS, "Mem Store" }, \ 31 {LOONGARCH_EXCEPTION_PIF, "Inst Fetch" }, \ 32 {LOONGARCH_EXCEPTION_PME, "Mem Modify" }, \ 33 {LOONGARCH_EXCEPTION_FPD, "FPU" }, \ 34 {LOONGARCH_EXCEPTION_SXD, "LSX" }, \ 35 {LOONGARCH_EXCEPTION_ASXD, "LASX" }, \ 36 {LOONGARCH_EXCEPTION_GSPR, "Privilege Error" }, \ 37 {LOONGARCH_EXCEPTION_HVC, "Hypercall" }, \ 38 {LOONGARCH_EXCEPTION_CPUCFG, "CPUCFG" }, \ 39 {LOONGARCH_EXCEPTION_CSR, "CSR" }, \ 40 {LOONGARCH_EXCEPTION_IOCSR, "IOCSR" }, \ 41 {LOONGARCH_EXCEPTION_IDLE, "Idle" }, \ 42 {LOONGARCH_EXCEPTION_OTHERS, "Others" } 43 44 define_exit_reasons_table(loongarch_exit_reasons, loongarch_exception_type); 45 46 const char *vcpu_id_str = "vcpu_id"; 47 const char *kvm_exit_reason = "reason"; 48 const char *kvm_entry_trace = "kvm:kvm_enter"; 49 const char *kvm_reenter_trace = "kvm:kvm_reenter"; 50 const char *kvm_exit_trace = "kvm:kvm_exit"; 51 const char *kvm_events_tp[] = { 52 "kvm:kvm_enter", 53 "kvm:kvm_reenter", 54 "kvm:kvm_exit", 55 "kvm:kvm_exit_gspr", 56 NULL, 57 }; 58 59 static bool event_begin(struct evsel *evsel, 60 struct perf_sample *sample, struct event_key *key) 61 { 62 return exit_event_begin(evsel, sample, key); 63 } 64 65 static bool event_end(struct evsel *evsel, 66 struct perf_sample *sample __maybe_unused, 67 struct event_key *key __maybe_unused) 68 { 69 /* 70 * LoongArch kvm is different with other architectures 71 * 72 * There is kvm:kvm_reenter or kvm:kvm_enter event adjacent with 73 * kvm:kvm_exit event. 74 * kvm:kvm_enter means returning to vmm and then to guest 75 * kvm:kvm_reenter means returning to guest immediately 76 */ 77 return evsel__name_is(evsel, kvm_entry_trace) || evsel__name_is(evsel, kvm_reenter_trace); 78 } 79 80 static void event_gspr_get_key(struct evsel *evsel, 81 struct perf_sample *sample, struct event_key *key) 82 { 83 unsigned int insn; 84 85 key->key = LOONGARCH_EXCEPTION_OTHERS; 86 insn = evsel__intval(evsel, sample, "inst_word"); 87 88 switch (insn >> 24) { 89 case 0: 90 /* CPUCFG inst trap */ 91 if ((insn >> 10) == 0x1b) 92 key->key = LOONGARCH_EXCEPTION_CPUCFG; 93 break; 94 case 4: 95 /* CSR inst trap */ 96 key->key = LOONGARCH_EXCEPTION_CSR; 97 break; 98 case 6: 99 /* IOCSR inst trap */ 100 if ((insn >> 15) == 0xc90) 101 key->key = LOONGARCH_EXCEPTION_IOCSR; 102 else if ((insn >> 15) == 0xc91) 103 /* Idle inst trap */ 104 key->key = LOONGARCH_EXCEPTION_IDLE; 105 break; 106 default: 107 key->key = LOONGARCH_EXCEPTION_OTHERS; 108 break; 109 } 110 } 111 112 static struct child_event_ops child_events[] = { 113 { .name = "kvm:kvm_exit_gspr", .get_key = event_gspr_get_key }, 114 { NULL, NULL }, 115 }; 116 117 static struct kvm_events_ops exit_events = { 118 .is_begin_event = event_begin, 119 .is_end_event = event_end, 120 .child_ops = child_events, 121 .decode_key = exit_event_decode_key, 122 .name = "VM-EXIT" 123 }; 124 125 struct kvm_reg_events_ops kvm_reg_events_ops[] = { 126 { .name = "vmexit", .ops = &exit_events, }, 127 { NULL, NULL }, 128 }; 129 130 const char * const kvm_skip_events[] = { 131 NULL, 132 }; 133 134 int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused) 135 { 136 kvm->exit_reasons_isa = "loongarch64"; 137 kvm->exit_reasons = loongarch_exit_reasons; 138 return 0; 139 } 140