xref: /kvm-unit-tests/x86/vmware_backdoors.c (revision a7f32d87d1e88e9c456d283e4b7235aa361753d7)
1907bb0b6SArbel Moshe 
2907bb0b6SArbel Moshe #include "x86/msr.h"
3907bb0b6SArbel Moshe #include "x86/processor.h"
4907bb0b6SArbel Moshe #include "x86/apic-defs.h"
5907bb0b6SArbel Moshe #include "x86/apic.h"
6907bb0b6SArbel Moshe #include "x86/desc.h"
7907bb0b6SArbel Moshe #include "x86/isr.h"
8907bb0b6SArbel Moshe #include "alloc.h"
9907bb0b6SArbel Moshe #include "setjmp.h"
10907bb0b6SArbel Moshe #include "usermode.h"
11907bb0b6SArbel Moshe #include "fault_test.h"
12907bb0b6SArbel Moshe 
13907bb0b6SArbel Moshe #include "libcflat.h"
14907bb0b6SArbel Moshe #include <stdint.h>
15907bb0b6SArbel Moshe 
16907bb0b6SArbel Moshe #define VMWARE_BACKDOOR_PMC_HOST_TSC           0x10000
17907bb0b6SArbel Moshe #define VMWARE_BACKDOOR_PMC_REAL_TIME          0x10001
18907bb0b6SArbel Moshe #define VMWARE_BACKDOOR_PMC_APPARENT_TIME      0x10002
19907bb0b6SArbel Moshe 
20907bb0b6SArbel Moshe #define VMWARE_BACKDOOR_PORT	0x5658
21907bb0b6SArbel Moshe #define VMWARE_MAGIC		0x564D5868
22907bb0b6SArbel Moshe 
23907bb0b6SArbel Moshe #define VMPORT_CMD_GETVERSION	0x0a
24907bb0b6SArbel Moshe #define VMPORT_CMD_ILLEGAL	0xfff
25907bb0b6SArbel Moshe 
26907bb0b6SArbel Moshe #define VMPORT_DEFAULT_RETVAL	0xdeadbeef
27907bb0b6SArbel Moshe 
28907bb0b6SArbel Moshe #define RANDOM_IO_PORT		0x1234
29907bb0b6SArbel Moshe 
30907bb0b6SArbel Moshe struct backdoor_port_result {
31907bb0b6SArbel Moshe 	uint64_t rax;
32907bb0b6SArbel Moshe 	uint64_t rbx;
33907bb0b6SArbel Moshe 	uint64_t rcx;
34907bb0b6SArbel Moshe 	uint64_t rdx;
35907bb0b6SArbel Moshe };
36907bb0b6SArbel Moshe 
37907bb0b6SArbel Moshe static bool vmware_backdoor_port_callback(struct fault_test_arg *arg)
38907bb0b6SArbel Moshe {
39907bb0b6SArbel Moshe 	struct backdoor_port_result *res =
40907bb0b6SArbel Moshe 		(struct backdoor_port_result *) arg->retval;
41907bb0b6SArbel Moshe 
42907bb0b6SArbel Moshe 	switch (arg->arg[2]) {
43907bb0b6SArbel Moshe 	case VMPORT_CMD_GETVERSION:
44907bb0b6SArbel Moshe 		return (res->rbx == VMWARE_MAGIC);
45907bb0b6SArbel Moshe 	case VMPORT_CMD_ILLEGAL:
46907bb0b6SArbel Moshe 		return (res->rbx == VMPORT_DEFAULT_RETVAL);
47907bb0b6SArbel Moshe 	}
48907bb0b6SArbel Moshe 	return false;
49907bb0b6SArbel Moshe }
50907bb0b6SArbel Moshe 
51907bb0b6SArbel Moshe static uint64_t vmware_backdoor_port(uint64_t vmport, uint64_t vmport_magic,
52907bb0b6SArbel Moshe 		uint64_t command)
53907bb0b6SArbel Moshe {
54907bb0b6SArbel Moshe 	struct backdoor_port_result *res =
55907bb0b6SArbel Moshe 		(struct backdoor_port_result *)
56907bb0b6SArbel Moshe 		malloc(sizeof(struct backdoor_port_result));
57907bb0b6SArbel Moshe 
58907bb0b6SArbel Moshe 	res->rax = VMPORT_DEFAULT_RETVAL;
59907bb0b6SArbel Moshe 	res->rbx = VMPORT_DEFAULT_RETVAL;
60907bb0b6SArbel Moshe 	res->rcx = VMPORT_DEFAULT_RETVAL;
61907bb0b6SArbel Moshe 	res->rdx = VMPORT_DEFAULT_RETVAL;
62907bb0b6SArbel Moshe 
63907bb0b6SArbel Moshe 	asm volatile(
64907bb0b6SArbel Moshe 		"mov %[rax], %%rax\n\t"
65907bb0b6SArbel Moshe 		"mov %[rdx], %%rdx\n\t"
66907bb0b6SArbel Moshe 		"mov %[rcx], %%rcx\n\t"
67907bb0b6SArbel Moshe 		"inl %%dx, %%eax\n\t"
68907bb0b6SArbel Moshe 		:
69907bb0b6SArbel Moshe 		"+a"(res->rax),
70907bb0b6SArbel Moshe 		"+b"(res->rbx),
71907bb0b6SArbel Moshe 		"+c"(res->rcx),
72907bb0b6SArbel Moshe 		"+d"(res->rdx)
73907bb0b6SArbel Moshe 		:
74907bb0b6SArbel Moshe 		[rax]"m"(vmport_magic),
75907bb0b6SArbel Moshe 		[rdx]"m"(vmport),
76907bb0b6SArbel Moshe 		[rcx]"m"(command)
77907bb0b6SArbel Moshe 		);
78907bb0b6SArbel Moshe 
79907bb0b6SArbel Moshe 	return (uint64_t) res;
80907bb0b6SArbel Moshe }
81907bb0b6SArbel Moshe 
82907bb0b6SArbel Moshe #define FAULT		true
83907bb0b6SArbel Moshe #define NO_FAULT	false
84907bb0b6SArbel Moshe #define USER_MODE	true
85907bb0b6SArbel Moshe #define KERNEL_MODE	false
86907bb0b6SArbel Moshe 
87907bb0b6SArbel Moshe #define RDPMC_ARG(n, m, sf) {.usermode = m, \
88907bb0b6SArbel Moshe 	.func =  (test_fault_func) rdpmc, .fault_vector = GP_VECTOR, \
89907bb0b6SArbel Moshe 	.should_fault = sf, .arg = {n, 0, 0, 0}, .callback = NULL}
90907bb0b6SArbel Moshe 
91907bb0b6SArbel Moshe #define RDPMC_TEST(name, a, m, sf) FAULT_TEST("rdpmc_test: "name, \
92907bb0b6SArbel Moshe 		RDPMC_ARG(a, m, sf))
93907bb0b6SArbel Moshe 
94907bb0b6SArbel Moshe #define PORT_ARG(a, b, c, m, sf) {.usermode = m, \
95907bb0b6SArbel Moshe 	.func =  (test_fault_func) vmware_backdoor_port, \
96907bb0b6SArbel Moshe 	.fault_vector = GP_VECTOR, .should_fault = sf, .arg = {a, b, c, 0}, \
97907bb0b6SArbel Moshe 	.callback = vmware_backdoor_port_callback}
98907bb0b6SArbel Moshe 
99907bb0b6SArbel Moshe #define PORT_TEST(name, a, b, c, m, sf) FAULT_TEST("port_test: "name, \
100907bb0b6SArbel Moshe 		PORT_ARG(a, b, c, m, sf))
101907bb0b6SArbel Moshe 
102907bb0b6SArbel Moshe 
103907bb0b6SArbel Moshe struct fault_test vmware_backdoor_tests[] = {
104907bb0b6SArbel Moshe 	RDPMC_TEST("HOST_TSC kernel", VMWARE_BACKDOOR_PMC_HOST_TSC,
105907bb0b6SArbel Moshe 			KERNEL_MODE, NO_FAULT),
106907bb0b6SArbel Moshe 	RDPMC_TEST("REAL_TIME kernel", VMWARE_BACKDOOR_PMC_REAL_TIME,
107907bb0b6SArbel Moshe 			KERNEL_MODE, NO_FAULT),
108907bb0b6SArbel Moshe 	RDPMC_TEST("APPARENT_TIME kernel", VMWARE_BACKDOOR_PMC_APPARENT_TIME,
109907bb0b6SArbel Moshe 			KERNEL_MODE, NO_FAULT),
110907bb0b6SArbel Moshe 	RDPMC_TEST("HOST_TSC user", VMWARE_BACKDOOR_PMC_HOST_TSC,
111907bb0b6SArbel Moshe 			USER_MODE, NO_FAULT),
112907bb0b6SArbel Moshe 	RDPMC_TEST("REAL_TIME user", VMWARE_BACKDOOR_PMC_REAL_TIME,
113907bb0b6SArbel Moshe 			USER_MODE, NO_FAULT),
114907bb0b6SArbel Moshe 	RDPMC_TEST("APPARENT_TIME user", VMWARE_BACKDOOR_PMC_APPARENT_TIME,
115907bb0b6SArbel Moshe 			USER_MODE, NO_FAULT),
116907bb0b6SArbel Moshe 	RDPMC_TEST("RANDOM PMC user", 0xfff, USER_MODE, FAULT),
117907bb0b6SArbel Moshe 
118907bb0b6SArbel Moshe 	PORT_TEST("CMD_GETVERSION user", VMWARE_BACKDOOR_PORT, VMWARE_MAGIC,
119907bb0b6SArbel Moshe 			VMPORT_CMD_GETVERSION, USER_MODE, NO_FAULT),
120907bb0b6SArbel Moshe 	PORT_TEST("CMD_GETVERSION kernel", VMWARE_BACKDOOR_PORT, VMWARE_MAGIC,
121907bb0b6SArbel Moshe 			VMPORT_CMD_GETVERSION, KERNEL_MODE, NO_FAULT),
122907bb0b6SArbel Moshe 	PORT_TEST("CMD_ILLEGAL user", VMWARE_BACKDOOR_PORT, VMWARE_MAGIC,
123907bb0b6SArbel Moshe 			VMPORT_CMD_ILLEGAL, USER_MODE, NO_FAULT),
124907bb0b6SArbel Moshe 	PORT_TEST("RANDOM port user", RANDOM_IO_PORT, VMWARE_MAGIC, 0xfff,
125907bb0b6SArbel Moshe 			USER_MODE, FAULT),
126907bb0b6SArbel Moshe 	{ NULL },
127907bb0b6SArbel Moshe };
128907bb0b6SArbel Moshe 
129907bb0b6SArbel Moshe /*
130907bb0b6SArbel Moshe  * Set TSS IO Perm to throw GP on RANDOM_IO_PORT and VMWARE_BACKDOOR_PORT
131907bb0b6SArbel Moshe  * from User Mode
132907bb0b6SArbel Moshe  */
133907bb0b6SArbel Moshe static void set_tss_ioperm(void)
134907bb0b6SArbel Moshe {
135*a7f32d87SPaolo Bonzini 	gdt_entry_t *tss_entry;
136907bb0b6SArbel Moshe 	tss64_t *tss;
137907bb0b6SArbel Moshe 	unsigned char *ioperm_bitmap;
138*a7f32d87SPaolo Bonzini 	u16 tr = str();
139907bb0b6SArbel Moshe 
140*a7f32d87SPaolo Bonzini 	tss_entry = get_tss_descr();
141*a7f32d87SPaolo Bonzini 	tss = (tss64_t *)get_gdt_entry_base(tss_entry);
142907bb0b6SArbel Moshe 	tss->iomap_base = sizeof(*tss);
143907bb0b6SArbel Moshe 	ioperm_bitmap = ((unsigned char *)tss+tss->iomap_base);
144907bb0b6SArbel Moshe 
145907bb0b6SArbel Moshe 	/* We want GP on RANDOM_IO_PORT and VMWARE_BACKDOOR_PORT */
146907bb0b6SArbel Moshe 	ioperm_bitmap[RANDOM_IO_PORT / 8] |=
147907bb0b6SArbel Moshe 		1 << (RANDOM_IO_PORT % 8);
148907bb0b6SArbel Moshe 	ioperm_bitmap[VMWARE_BACKDOOR_PORT / 8] |=
149907bb0b6SArbel Moshe 		1 << (VMWARE_BACKDOOR_PORT % 8);
150*a7f32d87SPaolo Bonzini 	tss_entry->type &= ~DESC_BUSY;
151907bb0b6SArbel Moshe 
152907bb0b6SArbel Moshe 	/* Update TSS */
153907bb0b6SArbel Moshe 	ltr(tr);
154907bb0b6SArbel Moshe }
155907bb0b6SArbel Moshe 
156907bb0b6SArbel Moshe static void check_vmware_backdoors(void)
157907bb0b6SArbel Moshe {
158907bb0b6SArbel Moshe 	int i;
159907bb0b6SArbel Moshe 
160907bb0b6SArbel Moshe 	/* Disable Permissions for IO PORTS */
161907bb0b6SArbel Moshe 	set_tss_ioperm();
162907bb0b6SArbel Moshe 	/* Disable Permission to run rdpmc from user mode */
163907bb0b6SArbel Moshe 	write_cr4(read_cr4() & ~X86_CR4_PCE);
164907bb0b6SArbel Moshe 
165907bb0b6SArbel Moshe 	report_prefix_push("vmware_backdoors");
166907bb0b6SArbel Moshe 
167907bb0b6SArbel Moshe 	for (i = 0; vmware_backdoor_tests[i].name != NULL; i++)
168907bb0b6SArbel Moshe 		test_run(&vmware_backdoor_tests[i]);
169907bb0b6SArbel Moshe 
170907bb0b6SArbel Moshe 	report_prefix_pop();
171907bb0b6SArbel Moshe }
172907bb0b6SArbel Moshe 
173907bb0b6SArbel Moshe int main(int ac, char **av)
174907bb0b6SArbel Moshe {
175907bb0b6SArbel Moshe 	setup_vm();
176907bb0b6SArbel Moshe 
177907bb0b6SArbel Moshe 	check_vmware_backdoors();
178907bb0b6SArbel Moshe 
179907bb0b6SArbel Moshe 	return report_summary();
180907bb0b6SArbel Moshe }
181