1/*
2 * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
3 *
4 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation;
8 * version 2.1 of the License (not later!)
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not,  see <http://www.gnu.org/licenses>
17 *
18 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19 */
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <stdint.h>
24
25#include "event-parse.h"
26
27#ifdef HAVE_UDIS86
28
29#include <udis86.h>
30
31static ud_t ud;
32
33static void init_disassembler(void)
34{
35	ud_init(&ud);
36	ud_set_syntax(&ud, UD_SYN_ATT);
37}
38
39static const char *disassemble(unsigned char *insn, int len, uint64_t rip,
40			       int cr0_pe, int eflags_vm,
41			       int cs_d, int cs_l)
42{
43	int mode;
44
45	if (!cr0_pe)
46		mode = 16;
47	else if (eflags_vm)
48		mode = 16;
49	else if (cs_l)
50		mode = 64;
51	else if (cs_d)
52		mode = 32;
53	else
54		mode = 16;
55
56	ud_set_pc(&ud, rip);
57	ud_set_mode(&ud, mode);
58	ud_set_input_buffer(&ud, insn, len);
59	ud_disassemble(&ud);
60	return ud_insn_asm(&ud);
61}
62
63#else
64
65static void init_disassembler(void)
66{
67}
68
69static const char *disassemble(unsigned char *insn, int len, uint64_t rip,
70			       int cr0_pe, int eflags_vm,
71			       int cs_d, int cs_l)
72{
73	static char out[15*3+1];
74	int i;
75
76	for (i = 0; i < len; ++i)
77		sprintf(out + i * 3, "%02x ", insn[i]);
78	out[len*3-1] = '\0';
79	return out;
80}
81
82#endif
83
84
85#define VMX_EXIT_REASONS			\
86	_ER(EXCEPTION_NMI,	 0)		\
87	_ER(EXTERNAL_INTERRUPT,	 1)		\
88	_ER(TRIPLE_FAULT,	 2)		\
89	_ER(PENDING_INTERRUPT,	 7)		\
90	_ER(NMI_WINDOW,		 8)		\
91	_ER(TASK_SWITCH,	 9)		\
92	_ER(CPUID,		 10)		\
93	_ER(HLT,		 12)		\
94	_ER(INVD,		 13)		\
95	_ER(INVLPG,		 14)		\
96	_ER(RDPMC,		 15)		\
97	_ER(RDTSC,		 16)		\
98	_ER(VMCALL,		 18)		\
99	_ER(VMCLEAR,		 19)		\
100	_ER(VMLAUNCH,		 20)		\
101	_ER(VMPTRLD,		 21)		\
102	_ER(VMPTRST,		 22)		\
103	_ER(VMREAD,		 23)		\
104	_ER(VMRESUME,		 24)		\
105	_ER(VMWRITE,		 25)		\
106	_ER(VMOFF,		 26)		\
107	_ER(VMON,		 27)		\
108	_ER(CR_ACCESS,		 28)		\
109	_ER(DR_ACCESS,		 29)		\
110	_ER(IO_INSTRUCTION,	 30)		\
111	_ER(MSR_READ,		 31)		\
112	_ER(MSR_WRITE,		 32)		\
113	_ER(MWAIT_INSTRUCTION,	 36)		\
114	_ER(MONITOR_INSTRUCTION, 39)		\
115	_ER(PAUSE_INSTRUCTION,	 40)		\
116	_ER(MCE_DURING_VMENTRY,	 41)		\
117	_ER(TPR_BELOW_THRESHOLD, 43)		\
118	_ER(APIC_ACCESS,	 44)		\
119	_ER(EOI_INDUCED,	 45)		\
120	_ER(EPT_VIOLATION,	 48)		\
121	_ER(EPT_MISCONFIG,	 49)		\
122	_ER(INVEPT,		 50)		\
123	_ER(PREEMPTION_TIMER,	 52)		\
124	_ER(WBINVD,		 54)		\
125	_ER(XSETBV,		 55)		\
126	_ER(APIC_WRITE,		 56)		\
127	_ER(INVPCID,		 58)
128
129#define SVM_EXIT_REASONS \
130	_ER(EXIT_READ_CR0,	0x000)		\
131	_ER(EXIT_READ_CR3,	0x003)		\
132	_ER(EXIT_READ_CR4,	0x004)		\
133	_ER(EXIT_READ_CR8,	0x008)		\
134	_ER(EXIT_WRITE_CR0,	0x010)		\
135	_ER(EXIT_WRITE_CR3,	0x013)		\
136	_ER(EXIT_WRITE_CR4,	0x014)		\
137	_ER(EXIT_WRITE_CR8,	0x018)		\
138	_ER(EXIT_READ_DR0,	0x020)		\
139	_ER(EXIT_READ_DR1,	0x021)		\
140	_ER(EXIT_READ_DR2,	0x022)		\
141	_ER(EXIT_READ_DR3,	0x023)		\
142	_ER(EXIT_READ_DR4,	0x024)		\
143	_ER(EXIT_READ_DR5,	0x025)		\
144	_ER(EXIT_READ_DR6,	0x026)		\
145	_ER(EXIT_READ_DR7,	0x027)		\
146	_ER(EXIT_WRITE_DR0,	0x030)		\
147	_ER(EXIT_WRITE_DR1,	0x031)		\
148	_ER(EXIT_WRITE_DR2,	0x032)		\
149	_ER(EXIT_WRITE_DR3,	0x033)		\
150	_ER(EXIT_WRITE_DR4,	0x034)		\
151	_ER(EXIT_WRITE_DR5,	0x035)		\
152	_ER(EXIT_WRITE_DR6,	0x036)		\
153	_ER(EXIT_WRITE_DR7,	0x037)		\
154	_ER(EXIT_EXCP_BASE,     0x040)		\
155	_ER(EXIT_INTR,		0x060)		\
156	_ER(EXIT_NMI,		0x061)		\
157	_ER(EXIT_SMI,		0x062)		\
158	_ER(EXIT_INIT,		0x063)		\
159	_ER(EXIT_VINTR,		0x064)		\
160	_ER(EXIT_CR0_SEL_WRITE,	0x065)		\
161	_ER(EXIT_IDTR_READ,	0x066)		\
162	_ER(EXIT_GDTR_READ,	0x067)		\
163	_ER(EXIT_LDTR_READ,	0x068)		\
164	_ER(EXIT_TR_READ,	0x069)		\
165	_ER(EXIT_IDTR_WRITE,	0x06a)		\
166	_ER(EXIT_GDTR_WRITE,	0x06b)		\
167	_ER(EXIT_LDTR_WRITE,	0x06c)		\
168	_ER(EXIT_TR_WRITE,	0x06d)		\
169	_ER(EXIT_RDTSC,		0x06e)		\
170	_ER(EXIT_RDPMC,		0x06f)		\
171	_ER(EXIT_PUSHF,		0x070)		\
172	_ER(EXIT_POPF,		0x071)		\
173	_ER(EXIT_CPUID,		0x072)		\
174	_ER(EXIT_RSM,		0x073)		\
175	_ER(EXIT_IRET,		0x074)		\
176	_ER(EXIT_SWINT,		0x075)		\
177	_ER(EXIT_INVD,		0x076)		\
178	_ER(EXIT_PAUSE,		0x077)		\
179	_ER(EXIT_HLT,		0x078)		\
180	_ER(EXIT_INVLPG,	0x079)		\
181	_ER(EXIT_INVLPGA,	0x07a)		\
182	_ER(EXIT_IOIO,		0x07b)		\
183	_ER(EXIT_MSR,		0x07c)		\
184	_ER(EXIT_TASK_SWITCH,	0x07d)		\
185	_ER(EXIT_FERR_FREEZE,	0x07e)		\
186	_ER(EXIT_SHUTDOWN,	0x07f)		\
187	_ER(EXIT_VMRUN,		0x080)		\
188	_ER(EXIT_VMMCALL,	0x081)		\
189	_ER(EXIT_VMLOAD,	0x082)		\
190	_ER(EXIT_VMSAVE,	0x083)		\
191	_ER(EXIT_STGI,		0x084)		\
192	_ER(EXIT_CLGI,		0x085)		\
193	_ER(EXIT_SKINIT,	0x086)		\
194	_ER(EXIT_RDTSCP,	0x087)		\
195	_ER(EXIT_ICEBP,		0x088)		\
196	_ER(EXIT_WBINVD,	0x089)		\
197	_ER(EXIT_MONITOR,	0x08a)		\
198	_ER(EXIT_MWAIT,		0x08b)		\
199	_ER(EXIT_MWAIT_COND,	0x08c)		\
200	_ER(EXIT_NPF,		0x400)		\
201	_ER(EXIT_ERR,		-1)
202
203#define _ER(reason, val)	{ #reason, val },
204struct str_values {
205	const char	*str;
206	int		val;
207};
208
209static struct str_values vmx_exit_reasons[] = {
210	VMX_EXIT_REASONS
211	{ NULL, -1}
212};
213
214static struct str_values svm_exit_reasons[] = {
215	SVM_EXIT_REASONS
216	{ NULL, -1}
217};
218
219static struct isa_exit_reasons {
220	unsigned isa;
221	struct str_values *strings;
222} isa_exit_reasons[] = {
223	{ .isa = 1, .strings = vmx_exit_reasons },
224	{ .isa = 2, .strings = svm_exit_reasons },
225	{ }
226};
227
228static const char *find_exit_reason(unsigned isa, int val)
229{
230	struct str_values *strings = NULL;
231	int i;
232
233	for (i = 0; isa_exit_reasons[i].strings; ++i)
234		if (isa_exit_reasons[i].isa == isa) {
235			strings = isa_exit_reasons[i].strings;
236			break;
237		}
238	if (!strings)
239		return "UNKNOWN-ISA";
240	for (i = 0; strings[i].val >= 0; i++)
241		if (strings[i].val == val)
242			break;
243
244	return strings[i].str;
245}
246
247static int print_exit_reason(struct trace_seq *s, struct pevent_record *record,
248			     struct event_format *event, const char *field)
249{
250	unsigned long long isa;
251	unsigned long long val;
252	const char *reason;
253
254	if (pevent_get_field_val(s, event, field, record, &val, 1) < 0)
255		return -1;
256
257	if (pevent_get_field_val(s, event, "isa", record, &isa, 0) < 0)
258		isa = 1;
259
260	reason = find_exit_reason(isa, val);
261	if (reason)
262		trace_seq_printf(s, "reason %s", reason);
263	else
264		trace_seq_printf(s, "reason UNKNOWN (%llu)", val);
265	return 0;
266}
267
268static int kvm_exit_handler(struct trace_seq *s, struct pevent_record *record,
269			    struct event_format *event, void *context)
270{
271	unsigned long long info1 = 0, info2 = 0;
272
273	if (print_exit_reason(s, record, event, "exit_reason") < 0)
274		return -1;
275
276	pevent_print_num_field(s, " rip 0x%lx", event, "guest_rip", record, 1);
277
278	if (pevent_get_field_val(s, event, "info1", record, &info1, 0) >= 0
279	    && pevent_get_field_val(s, event, "info2", record, &info2, 0) >= 0)
280		trace_seq_printf(s, " info %llx %llx", info1, info2);
281
282	return 0;
283}
284
285#define KVM_EMUL_INSN_F_CR0_PE (1 << 0)
286#define KVM_EMUL_INSN_F_EFL_VM (1 << 1)
287#define KVM_EMUL_INSN_F_CS_D   (1 << 2)
288#define KVM_EMUL_INSN_F_CS_L   (1 << 3)
289
290static int kvm_emulate_insn_handler(struct trace_seq *s,
291				    struct pevent_record *record,
292				    struct event_format *event, void *context)
293{
294	unsigned long long rip, csbase, len, flags, failed;
295	int llen;
296	uint8_t *insn;
297	const char *disasm;
298
299	if (pevent_get_field_val(s, event, "rip", record, &rip, 1) < 0)
300		return -1;
301
302	if (pevent_get_field_val(s, event, "csbase", record, &csbase, 1) < 0)
303		return -1;
304
305	if (pevent_get_field_val(s, event, "len", record, &len, 1) < 0)
306		return -1;
307
308	if (pevent_get_field_val(s, event, "flags", record, &flags, 1) < 0)
309		return -1;
310
311	if (pevent_get_field_val(s, event, "failed", record, &failed, 1) < 0)
312		return -1;
313
314	insn = pevent_get_field_raw(s, event, "insn", record, &llen, 1);
315	if (!insn)
316		return -1;
317
318	disasm = disassemble(insn, len, rip,
319			     flags & KVM_EMUL_INSN_F_CR0_PE,
320			     flags & KVM_EMUL_INSN_F_EFL_VM,
321			     flags & KVM_EMUL_INSN_F_CS_D,
322			     flags & KVM_EMUL_INSN_F_CS_L);
323
324	trace_seq_printf(s, "%llx:%llx: %s%s", csbase, rip, disasm,
325			 failed ? " FAIL" : "");
326	return 0;
327}
328
329
330static int kvm_nested_vmexit_inject_handler(struct trace_seq *s, struct pevent_record *record,
331					    struct event_format *event, void *context)
332{
333	if (print_exit_reason(s, record, event, "exit_code") < 0)
334		return -1;
335
336	pevent_print_num_field(s, " info1 %llx", event, "exit_info1", record, 1);
337	pevent_print_num_field(s, " info2 %llx", event, "exit_info2", record, 1);
338	pevent_print_num_field(s, " int_info %llx", event, "exit_int_info", record, 1);
339	pevent_print_num_field(s, " int_info_err %llx", event, "exit_int_info_err", record, 1);
340
341	return 0;
342}
343
344static int kvm_nested_vmexit_handler(struct trace_seq *s, struct pevent_record *record,
345				     struct event_format *event, void *context)
346{
347	pevent_print_num_field(s, "rip %llx ", event, "rip", record, 1);
348
349	return kvm_nested_vmexit_inject_handler(s, record, event, context);
350}
351
352union kvm_mmu_page_role {
353	unsigned word;
354	struct {
355		unsigned glevels:4;
356		unsigned level:4;
357		unsigned quadrant:2;
358		unsigned pad_for_nice_hex_output:6;
359		unsigned direct:1;
360		unsigned access:3;
361		unsigned invalid:1;
362		unsigned cr4_pge:1;
363		unsigned nxe:1;
364	};
365};
366
367static int kvm_mmu_print_role(struct trace_seq *s, struct pevent_record *record,
368			      struct event_format *event, void *context)
369{
370	unsigned long long val;
371	static const char *access_str[] = {
372		"---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux"
373	};
374	union kvm_mmu_page_role role;
375
376	if (pevent_get_field_val(s, event, "role", record, &val, 1) < 0)
377		return -1;
378
379	role.word = (int)val;
380
381	/*
382	 * We can only use the structure if file is of the same
383	 * endianess.
384	 */
385	if (pevent_is_file_bigendian(event->pevent) ==
386	    pevent_is_host_bigendian(event->pevent)) {
387
388		trace_seq_printf(s, "%u/%u q%u%s %s%s %spge %snxe",
389				 role.level,
390				 role.glevels,
391				 role.quadrant,
392				 role.direct ? " direct" : "",
393				 access_str[role.access],
394				 role.invalid ? " invalid" : "",
395				 role.cr4_pge ? "" : "!",
396				 role.nxe ? "" : "!");
397	} else
398		trace_seq_printf(s, "WORD: %08x", role.word);
399
400	pevent_print_num_field(s, " root %u ",  event,
401			       "root_count", record, 1);
402
403	if (pevent_get_field_val(s, event, "unsync", record, &val, 1) < 0)
404		return -1;
405
406	trace_seq_printf(s, "%s%c",  val ? "unsync" : "sync", 0);
407	return 0;
408}
409
410static int kvm_mmu_get_page_handler(struct trace_seq *s,
411				    struct pevent_record *record,
412				    struct event_format *event, void *context)
413{
414	unsigned long long val;
415
416	if (pevent_get_field_val(s, event, "created", record, &val, 1) < 0)
417		return -1;
418
419	trace_seq_printf(s, "%s ", val ? "new" : "existing");
420
421	if (pevent_get_field_val(s, event, "gfn", record, &val, 1) < 0)
422		return -1;
423
424	trace_seq_printf(s, "sp gfn %llx ", val);
425	return kvm_mmu_print_role(s, record, event, context);
426}
427
428#define PT_WRITABLE_SHIFT 1
429#define PT_WRITABLE_MASK (1ULL << PT_WRITABLE_SHIFT)
430
431static unsigned long long
432process_is_writable_pte(struct trace_seq *s, unsigned long long *args)
433{
434	unsigned long pte = args[0];
435	return pte & PT_WRITABLE_MASK;
436}
437
438int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
439{
440	init_disassembler();
441
442	pevent_register_event_handler(pevent, -1, "kvm", "kvm_exit",
443				      kvm_exit_handler, NULL);
444
445	pevent_register_event_handler(pevent, -1, "kvm", "kvm_emulate_insn",
446				      kvm_emulate_insn_handler, NULL);
447
448	pevent_register_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit",
449				      kvm_nested_vmexit_handler, NULL);
450
451	pevent_register_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit_inject",
452				      kvm_nested_vmexit_inject_handler, NULL);
453
454	pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_get_page",
455				      kvm_mmu_get_page_handler, NULL);
456
457	pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_sync_page",
458				      kvm_mmu_print_role, NULL);
459
460	pevent_register_event_handler(pevent, -1,
461				      "kvmmmu", "kvm_mmu_unsync_page",
462				      kvm_mmu_print_role, NULL);
463
464	pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_zap_page",
465				      kvm_mmu_print_role, NULL);
466
467	pevent_register_event_handler(pevent, -1, "kvmmmu",
468			"kvm_mmu_prepare_zap_page", kvm_mmu_print_role,
469			NULL);
470
471	pevent_register_print_function(pevent,
472				       process_is_writable_pte,
473				       PEVENT_FUNC_ARG_INT,
474				       "is_writable_pte",
475				       PEVENT_FUNC_ARG_LONG,
476				       PEVENT_FUNC_ARG_VOID);
477	return 0;
478}
479
480void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
481{
482	pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_exit",
483					kvm_exit_handler, NULL);
484
485	pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_emulate_insn",
486					kvm_emulate_insn_handler, NULL);
487
488	pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit",
489					kvm_nested_vmexit_handler, NULL);
490
491	pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit_inject",
492					kvm_nested_vmexit_inject_handler, NULL);
493
494	pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_get_page",
495					kvm_mmu_get_page_handler, NULL);
496
497	pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_sync_page",
498					kvm_mmu_print_role, NULL);
499
500	pevent_unregister_event_handler(pevent, -1,
501					"kvmmmu", "kvm_mmu_unsync_page",
502					kvm_mmu_print_role, NULL);
503
504	pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_zap_page",
505					kvm_mmu_print_role, NULL);
506
507	pevent_unregister_event_handler(pevent, -1, "kvmmmu",
508			"kvm_mmu_prepare_zap_page", kvm_mmu_print_role,
509			NULL);
510
511	pevent_unregister_print_function(pevent, process_is_writable_pte,
512					 "is_writable_pte");
513}
514