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