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
event_begin(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)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
event_end(struct evsel * evsel,struct perf_sample * sample __maybe_unused,struct event_key * key __maybe_unused)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
event_gspr_get_key(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)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
cpu_isa_init(struct perf_kvm_stat * kvm,const char * cpuid __maybe_unused)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