xref: /kvm-unit-tests/x86/emulator64.c (revision cb0fabbb3c7a8a95824051b53e385fefe18e05cc)
1bbdb7433SSean Christopherson #define MAGIC_NUM 0xdeadbeefdeadbeefUL
2bbdb7433SSean Christopherson #define GS_BASE 0x400000
3bbdb7433SSean Christopherson 
4bbdb7433SSean Christopherson static unsigned long rip_advance;
5bbdb7433SSean Christopherson 
6bbdb7433SSean Christopherson static void advance_rip_and_note_exception(struct ex_regs *regs)
7bbdb7433SSean Christopherson {
8bbdb7433SSean Christopherson 	++exceptions;
9bbdb7433SSean Christopherson 	regs->rip += rip_advance;
10bbdb7433SSean Christopherson }
11bbdb7433SSean Christopherson 
12bbdb7433SSean Christopherson static void test_cr8(void)
13bbdb7433SSean Christopherson {
14bbdb7433SSean Christopherson 	unsigned long src, dst;
15bbdb7433SSean Christopherson 
16bbdb7433SSean Christopherson 	dst = 777;
17bbdb7433SSean Christopherson 	src = 3;
18bbdb7433SSean Christopherson 	asm volatile("mov %[src], %%cr8; mov %%cr8, %[dst]"
19bbdb7433SSean Christopherson 		     : [dst]"+r"(dst), [src]"+r"(src));
20bbdb7433SSean Christopherson 	report(dst == 3 && src == 3, "mov %%cr8");
21bbdb7433SSean Christopherson }
22bbdb7433SSean Christopherson 
23bbdb7433SSean Christopherson static void test_push(void *mem)
24bbdb7433SSean Christopherson {
25bbdb7433SSean Christopherson 	unsigned long tmp;
26bbdb7433SSean Christopherson 	unsigned long *stack_top = mem + 4096;
27bbdb7433SSean Christopherson 	unsigned long *new_stack_top;
28bbdb7433SSean Christopherson 	unsigned long memw = 0x123456789abcdeful;
29bbdb7433SSean Christopherson 
30bbdb7433SSean Christopherson 	memset(mem, 0x55, (void *)stack_top - mem);
31bbdb7433SSean Christopherson 
32bbdb7433SSean Christopherson 	asm volatile("mov %%rsp, %[tmp] \n\t"
33bbdb7433SSean Christopherson 		     "mov %[stack_top], %%rsp \n\t"
34bbdb7433SSean Christopherson 		     "pushq $-7 \n\t"
35bbdb7433SSean Christopherson 		     "pushq %[reg] \n\t"
36bbdb7433SSean Christopherson 		     "pushq (%[mem]) \n\t"
37bbdb7433SSean Christopherson 		     "pushq $-7070707 \n\t"
38bbdb7433SSean Christopherson 		     "mov %%rsp, %[new_stack_top] \n\t"
39bbdb7433SSean Christopherson 		     "mov %[tmp], %%rsp"
40bbdb7433SSean Christopherson 		     : [tmp]"=&r"(tmp), [new_stack_top]"=r"(new_stack_top)
41bbdb7433SSean Christopherson 		     : [stack_top]"r"(stack_top),
42bbdb7433SSean Christopherson 		       [reg]"r"(-17l), [mem]"r"(&memw)
43bbdb7433SSean Christopherson 		     : "memory");
44bbdb7433SSean Christopherson 
45bbdb7433SSean Christopherson 	report(stack_top[-1] == -7ul, "push $imm8");
46bbdb7433SSean Christopherson 	report(stack_top[-2] == -17ul, "push %%reg");
47bbdb7433SSean Christopherson 	report(stack_top[-3] == 0x123456789abcdeful, "push mem");
48bbdb7433SSean Christopherson 	report(stack_top[-4] == -7070707, "push $imm");
49bbdb7433SSean Christopherson }
50bbdb7433SSean Christopherson 
51bbdb7433SSean Christopherson static void test_pop(void *mem)
52bbdb7433SSean Christopherson {
53bbdb7433SSean Christopherson 	unsigned long tmp, tmp3, rsp, rbp;
54bbdb7433SSean Christopherson 	unsigned long *stack_top = mem + 4096;
55bbdb7433SSean Christopherson 	unsigned long memw = 0x123456789abcdeful;
56bbdb7433SSean Christopherson 	static unsigned long tmp2;
57bbdb7433SSean Christopherson 
58bbdb7433SSean Christopherson 	memset(mem, 0x55, (void *)stack_top - mem);
59bbdb7433SSean Christopherson 
60bbdb7433SSean Christopherson 	asm volatile("pushq %[val] \n\t"
61bbdb7433SSean Christopherson 		     "popq (%[mem])"
62bbdb7433SSean Christopherson 		     : : [val]"m"(memw), [mem]"r"(mem) : "memory");
63bbdb7433SSean Christopherson 	report(*(unsigned long *)mem == memw, "pop mem");
64bbdb7433SSean Christopherson 
65bbdb7433SSean Christopherson 	memw = 7 - memw;
66bbdb7433SSean Christopherson 	asm volatile("mov %%rsp, %[tmp] \n\t"
67bbdb7433SSean Christopherson 		     "mov %[stack_top], %%rsp \n\t"
68bbdb7433SSean Christopherson 		     "pushq %[val] \n\t"
69bbdb7433SSean Christopherson 		     "popq %[tmp2] \n\t"
70bbdb7433SSean Christopherson 		     "mov %[tmp], %%rsp"
71bbdb7433SSean Christopherson 		     : [tmp]"=&r"(tmp), [tmp2]"=m"(tmp2)
72bbdb7433SSean Christopherson 		     : [val]"r"(memw), [stack_top]"r"(stack_top)
73bbdb7433SSean Christopherson 		     : "memory");
74bbdb7433SSean Christopherson 	report(tmp2 == memw, "pop mem (2)");
75bbdb7433SSean Christopherson 
76bbdb7433SSean Christopherson 	memw = 129443 - memw;
77bbdb7433SSean Christopherson 	asm volatile("mov %%rsp, %[tmp] \n\t"
78bbdb7433SSean Christopherson 		     "mov %[stack_top], %%rsp \n\t"
79bbdb7433SSean Christopherson 		     "pushq %[val] \n\t"
80bbdb7433SSean Christopherson 		     "popq %[tmp2] \n\t"
81bbdb7433SSean Christopherson 		     "mov %[tmp], %%rsp"
82bbdb7433SSean Christopherson 		     : [tmp]"=&r"(tmp), [tmp2]"=r"(tmp2)
83bbdb7433SSean Christopherson 		     : [val]"r"(memw), [stack_top]"r"(stack_top)
84bbdb7433SSean Christopherson 		     : "memory");
85bbdb7433SSean Christopherson 	report(tmp2 == memw, "pop reg");
86bbdb7433SSean Christopherson 
87bbdb7433SSean Christopherson 	asm volatile("mov %%rsp, %[tmp] \n\t"
88bbdb7433SSean Christopherson 		     "mov %[stack_top], %%rsp \n\t"
89bbdb7433SSean Christopherson 		     "lea 1f(%%rip), %%rax \n\t"
90bbdb7433SSean Christopherson 		     "push %%rax \n\t"
91bbdb7433SSean Christopherson 		     "ret \n\t"
92bbdb7433SSean Christopherson 		     "2: jmp 2b \n\t"
93bbdb7433SSean Christopherson 		     "1: mov %[tmp], %%rsp"
94bbdb7433SSean Christopherson 		     : [tmp]"=&r"(tmp) : [stack_top]"r"(stack_top)
95bbdb7433SSean Christopherson 		     : "memory", "rax");
96bbdb7433SSean Christopherson 	report_pass("ret");
97bbdb7433SSean Christopherson 
98bbdb7433SSean Christopherson 	stack_top[-1] = 0x778899;
99bbdb7433SSean Christopherson 	asm volatile("mov %[stack_top], %%r8 \n\t"
100bbdb7433SSean Christopherson 		     "mov %%rsp, %%r9 \n\t"
101bbdb7433SSean Christopherson 		     "xchg %%rbp, %%r8 \n\t"
102bbdb7433SSean Christopherson 		     "leave \n\t"
103bbdb7433SSean Christopherson 		     "xchg %%rsp, %%r9 \n\t"
104bbdb7433SSean Christopherson 		     "xchg %%rbp, %%r8 \n\t"
105bbdb7433SSean Christopherson 		     "mov %%r9, %[tmp] \n\t"
106bbdb7433SSean Christopherson 		     "mov %%r8, %[tmp3]"
107bbdb7433SSean Christopherson 		     : [tmp]"=&r"(tmp), [tmp3]"=&r"(tmp3) : [stack_top]"r"(stack_top-1)
108bbdb7433SSean Christopherson 		     : "memory", "r8", "r9");
109bbdb7433SSean Christopherson 	report(tmp == (ulong)stack_top && tmp3 == 0x778899, "leave");
110bbdb7433SSean Christopherson 
111bbdb7433SSean Christopherson 	rbp = 0xaa55aa55bb66bb66ULL;
112bbdb7433SSean Christopherson 	rsp = (unsigned long)stack_top;
113bbdb7433SSean Christopherson 	asm volatile("mov %[rsp], %%r8 \n\t"
114bbdb7433SSean Christopherson 		     "mov %[rbp], %%r9 \n\t"
115bbdb7433SSean Christopherson 		     "xchg %%rsp, %%r8 \n\t"
116bbdb7433SSean Christopherson 		     "xchg %%rbp, %%r9 \n\t"
117bbdb7433SSean Christopherson 		     "enter $0x1238, $0 \n\t"
118bbdb7433SSean Christopherson 		     "xchg %%rsp, %%r8 \n\t"
119bbdb7433SSean Christopherson 		     "xchg %%rbp, %%r9 \n\t"
120bbdb7433SSean Christopherson 		     "xchg %%r8, %[rsp] \n\t"
121bbdb7433SSean Christopherson 		     "xchg %%r9, %[rbp]"
122bbdb7433SSean Christopherson 		     : [rsp]"+a"(rsp), [rbp]"+b"(rbp) : : "memory", "r8", "r9");
123bbdb7433SSean Christopherson 	report(rsp == (unsigned long)stack_top - 8 - 0x1238
124bbdb7433SSean Christopherson 	       && rbp == (unsigned long)stack_top - 8
125bbdb7433SSean Christopherson 	       && stack_top[-1] == 0xaa55aa55bb66bb66ULL,
126bbdb7433SSean Christopherson 	       "enter");
127bbdb7433SSean Christopherson }
128bbdb7433SSean Christopherson 
129bbdb7433SSean Christopherson static void test_ljmp(void *mem)
130bbdb7433SSean Christopherson {
131bbdb7433SSean Christopherson 	unsigned char *m = mem;
132bbdb7433SSean Christopherson 	volatile int res = 1;
133bbdb7433SSean Christopherson 
134bbdb7433SSean Christopherson 	*(unsigned long**)m = &&jmpf;
135bbdb7433SSean Christopherson 	asm volatile ("data16 mov %%cs, %0":"=m"(*(m + sizeof(unsigned long))));
136bbdb7433SSean Christopherson 	asm volatile ("rex64 ljmp *%0"::"m"(*m));
137bbdb7433SSean Christopherson 	res = 0;
138bbdb7433SSean Christopherson jmpf:
139bbdb7433SSean Christopherson 	report(res, "ljmp");
140bbdb7433SSean Christopherson }
141bbdb7433SSean Christopherson 
142bbdb7433SSean Christopherson static void test_xchg(void *mem)
143bbdb7433SSean Christopherson {
144bbdb7433SSean Christopherson 	unsigned long *memq = mem;
145bbdb7433SSean Christopherson 	unsigned long rax;
146bbdb7433SSean Christopherson 
147bbdb7433SSean Christopherson 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
148bbdb7433SSean Christopherson 		     "mov %%rax, (%[memq])\n\t"
149bbdb7433SSean Christopherson 		     "mov $0xfedcba9876543210, %%rax\n\t"
150bbdb7433SSean Christopherson 		     "xchg %%al, (%[memq])\n\t"
151bbdb7433SSean Christopherson 		     "mov %%rax, %[rax]\n\t"
152bbdb7433SSean Christopherson 		     : [rax]"=r"(rax)
153bbdb7433SSean Christopherson 		     : [memq]"r"(memq)
154bbdb7433SSean Christopherson 		     : "memory", "rax");
155bbdb7433SSean Christopherson 	report(rax == 0xfedcba98765432ef && *memq == 0x123456789abcd10,
156bbdb7433SSean Christopherson 	       "xchg reg, r/m (1)");
157bbdb7433SSean Christopherson 
158bbdb7433SSean Christopherson 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
159bbdb7433SSean Christopherson 		     "mov %%rax, (%[memq])\n\t"
160bbdb7433SSean Christopherson 		     "mov $0xfedcba9876543210, %%rax\n\t"
161bbdb7433SSean Christopherson 		     "xchg %%ax, (%[memq])\n\t"
162bbdb7433SSean Christopherson 		     "mov %%rax, %[rax]\n\t"
163bbdb7433SSean Christopherson 		     : [rax]"=r"(rax)
164bbdb7433SSean Christopherson 		     : [memq]"r"(memq)
165bbdb7433SSean Christopherson 		     : "memory", "rax");
166bbdb7433SSean Christopherson 	report(rax == 0xfedcba987654cdef && *memq == 0x123456789ab3210,
167bbdb7433SSean Christopherson 	       "xchg reg, r/m (2)");
168bbdb7433SSean Christopherson 
169bbdb7433SSean Christopherson 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
170bbdb7433SSean Christopherson 		     "mov %%rax, (%[memq])\n\t"
171bbdb7433SSean Christopherson 		     "mov $0xfedcba9876543210, %%rax\n\t"
172bbdb7433SSean Christopherson 		     "xchg %%eax, (%[memq])\n\t"
173bbdb7433SSean Christopherson 		     "mov %%rax, %[rax]\n\t"
174bbdb7433SSean Christopherson 		     : [rax]"=r"(rax)
175bbdb7433SSean Christopherson 		     : [memq]"r"(memq)
176bbdb7433SSean Christopherson 		     : "memory", "rax");
177bbdb7433SSean Christopherson 	report(rax == 0x89abcdef && *memq == 0x123456776543210,
178bbdb7433SSean Christopherson 	       "xchg reg, r/m (3)");
179bbdb7433SSean Christopherson 
180bbdb7433SSean Christopherson 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
181bbdb7433SSean Christopherson 		     "mov %%rax, (%[memq])\n\t"
182bbdb7433SSean Christopherson 		     "mov $0xfedcba9876543210, %%rax\n\t"
183bbdb7433SSean Christopherson 		     "xchg %%rax, (%[memq])\n\t"
184bbdb7433SSean Christopherson 		     "mov %%rax, %[rax]\n\t"
185bbdb7433SSean Christopherson 		     : [rax]"=r"(rax)
186bbdb7433SSean Christopherson 		     : [memq]"r"(memq)
187bbdb7433SSean Christopherson 		     : "memory", "rax");
188bbdb7433SSean Christopherson 	report(rax == 0x123456789abcdef && *memq == 0xfedcba9876543210,
189bbdb7433SSean Christopherson 	       "xchg reg, r/m (4)");
190bbdb7433SSean Christopherson }
191bbdb7433SSean Christopherson 
192bbdb7433SSean Christopherson static void test_xadd(void *mem)
193bbdb7433SSean Christopherson {
194bbdb7433SSean Christopherson 	unsigned long *memq = mem;
195bbdb7433SSean Christopherson 	unsigned long rax;
196bbdb7433SSean Christopherson 
197bbdb7433SSean Christopherson 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
198bbdb7433SSean Christopherson 		     "mov %%rax, (%[memq])\n\t"
199bbdb7433SSean Christopherson 		     "mov $0xfedcba9876543210, %%rax\n\t"
200bbdb7433SSean Christopherson 		     "xadd %%al, (%[memq])\n\t"
201bbdb7433SSean Christopherson 		     "mov %%rax, %[rax]\n\t"
202bbdb7433SSean Christopherson 		     : [rax]"=r"(rax)
203bbdb7433SSean Christopherson 		     : [memq]"r"(memq)
204bbdb7433SSean Christopherson 		     : "memory", "rax");
205bbdb7433SSean Christopherson 	report(rax == 0xfedcba98765432ef && *memq == 0x123456789abcdff,
206bbdb7433SSean Christopherson 	       "xadd reg, r/m (1)");
207bbdb7433SSean Christopherson 
208bbdb7433SSean Christopherson 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
209bbdb7433SSean Christopherson 		     "mov %%rax, (%[memq])\n\t"
210bbdb7433SSean Christopherson 		     "mov $0xfedcba9876543210, %%rax\n\t"
211bbdb7433SSean Christopherson 		     "xadd %%ax, (%[memq])\n\t"
212bbdb7433SSean Christopherson 		     "mov %%rax, %[rax]\n\t"
213bbdb7433SSean Christopherson 		     : [rax]"=r"(rax)
214bbdb7433SSean Christopherson 		     : [memq]"r"(memq)
215bbdb7433SSean Christopherson 		     : "memory", "rax");
216bbdb7433SSean Christopherson 	report(rax == 0xfedcba987654cdef && *memq == 0x123456789abffff,
217bbdb7433SSean Christopherson 	       "xadd reg, r/m (2)");
218bbdb7433SSean Christopherson 
219bbdb7433SSean Christopherson 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
220bbdb7433SSean Christopherson 		     "mov %%rax, (%[memq])\n\t"
221bbdb7433SSean Christopherson 		     "mov $0xfedcba9876543210, %%rax\n\t"
222bbdb7433SSean Christopherson 		     "xadd %%eax, (%[memq])\n\t"
223bbdb7433SSean Christopherson 		     "mov %%rax, %[rax]\n\t"
224bbdb7433SSean Christopherson 		     : [rax]"=r"(rax)
225bbdb7433SSean Christopherson 		     : [memq]"r"(memq)
226bbdb7433SSean Christopherson 		     : "memory", "rax");
227bbdb7433SSean Christopherson 	report(rax == 0x89abcdef && *memq == 0x1234567ffffffff,
228bbdb7433SSean Christopherson 	       "xadd reg, r/m (3)");
229bbdb7433SSean Christopherson 
230bbdb7433SSean Christopherson 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
231bbdb7433SSean Christopherson 		     "mov %%rax, (%[memq])\n\t"
232bbdb7433SSean Christopherson 		     "mov $0xfedcba9876543210, %%rax\n\t"
233bbdb7433SSean Christopherson 		     "xadd %%rax, (%[memq])\n\t"
234bbdb7433SSean Christopherson 		     "mov %%rax, %[rax]\n\t"
235bbdb7433SSean Christopherson 		     : [rax]"=r"(rax)
236bbdb7433SSean Christopherson 		     : [memq]"r"(memq)
237bbdb7433SSean Christopherson 		     : "memory", "rax");
238bbdb7433SSean Christopherson 	report(rax == 0x123456789abcdef && *memq == 0xffffffffffffffff,
239bbdb7433SSean Christopherson 	       "xadd reg, r/m (4)");
240bbdb7433SSean Christopherson }
241bbdb7433SSean Christopherson 
242bbdb7433SSean Christopherson static void test_muldiv(long *mem)
243bbdb7433SSean Christopherson {
244bbdb7433SSean Christopherson 	long a, d, aa, dd;
245bbdb7433SSean Christopherson 	u8 ex = 1;
246bbdb7433SSean Christopherson 
247bbdb7433SSean Christopherson 	*mem = 0; a = 1; d = 2;
248bbdb7433SSean Christopherson 	asm (ASM_TRY("1f") "divq %3; movb $0, %2; 1:"
249bbdb7433SSean Christopherson 	     : "+a"(a), "+d"(d), "+q"(ex) : "m"(*mem));
250bbdb7433SSean Christopherson 	report(a == 1 && d == 2 && ex, "divq (fault)");
251bbdb7433SSean Christopherson 
252bbdb7433SSean Christopherson 	*mem = 987654321098765UL; a = 123456789012345UL; d = 123456789012345UL;
253bbdb7433SSean Christopherson 	asm (ASM_TRY("1f") "divq %3; movb $0, %2; 1:"
254bbdb7433SSean Christopherson 	     : "+a"(a), "+d"(d), "+q"(ex) : "m"(*mem));
255bbdb7433SSean Christopherson 	report(a == 0x1ffffffb1b963b33ul && d == 0x273ba4384ede2ul && !ex, "divq (1)");
256bbdb7433SSean Christopherson 
257bbdb7433SSean Christopherson 	aa = 0x1111111111111111; dd = 0x2222222222222222;
258bbdb7433SSean Christopherson 	*mem = 0x3333333333333333; a = aa; d = dd;
259bbdb7433SSean Christopherson 	asm("mulb %2" : "+a"(a), "+d"(d) : "m"(*mem));
260bbdb7433SSean Christopherson 	report(a == 0x1111111111110363 && d == dd, "mulb mem");
261bbdb7433SSean Christopherson 	*mem = 0x3333333333333333; a = aa; d = dd;
262bbdb7433SSean Christopherson 	asm("mulw %2" : "+a"(a), "+d"(d) : "m"(*mem));
263bbdb7433SSean Christopherson 	report(a == 0x111111111111c963 && d == 0x2222222222220369, "mulw mem");
264bbdb7433SSean Christopherson 	*mem = 0x3333333333333333; a = aa; d = dd;
265bbdb7433SSean Christopherson 	asm("mull %2" : "+a"(a), "+d"(d) : "m"(*mem));
266bbdb7433SSean Christopherson 	report(a == 0x962fc963 && d == 0x369d036, "mull mem");
267bbdb7433SSean Christopherson 	*mem = 0x3333333333333333; a = aa; d = dd;
268bbdb7433SSean Christopherson 	asm("mulq %2" : "+a"(a), "+d"(d) : "m"(*mem));
269bbdb7433SSean Christopherson 	report(a == 0x2fc962fc962fc963 && d == 0x369d0369d0369d0, "mulq mem");
270bbdb7433SSean Christopherson }
271bbdb7433SSean Christopherson 
272bbdb7433SSean Christopherson static void test_mmx(uint64_t *mem)
273bbdb7433SSean Christopherson {
274bbdb7433SSean Christopherson 	uint64_t v;
275bbdb7433SSean Christopherson 
276bbdb7433SSean Christopherson 	write_cr0(read_cr0() & ~6); /* EM, TS */
277bbdb7433SSean Christopherson 	asm volatile("fninit");
278bbdb7433SSean Christopherson 	v = 0x0102030405060708ULL;
279bbdb7433SSean Christopherson 	asm("movq %1, %0" : "=m"(*mem) : "y"(v));
280bbdb7433SSean Christopherson 	report(v == *mem, "movq (mmx, read)");
281bbdb7433SSean Christopherson 	*mem = 0x8070605040302010ull;
282bbdb7433SSean Christopherson 	asm("movq %1, %0" : "=y"(v) : "m"(*mem));
283bbdb7433SSean Christopherson 	report(v == *mem, "movq (mmx, write)");
284bbdb7433SSean Christopherson }
285bbdb7433SSean Christopherson 
286bbdb7433SSean Christopherson static void test_rip_relative(unsigned *mem, char *insn_ram)
287bbdb7433SSean Christopherson {
288bbdb7433SSean Christopherson 	/* movb $1, mem+2(%rip) */
289bbdb7433SSean Christopherson 	insn_ram[0] = 0xc6;
290bbdb7433SSean Christopherson 	insn_ram[1] = 0x05;
291bbdb7433SSean Christopherson 	*(unsigned *)&insn_ram[2] = 2 + (char *)mem - (insn_ram + 7);
292bbdb7433SSean Christopherson 	insn_ram[6] = 0x01;
293bbdb7433SSean Christopherson 	/* ret */
294bbdb7433SSean Christopherson 	insn_ram[7] = 0xc3;
295bbdb7433SSean Christopherson 
296bbdb7433SSean Christopherson 	*mem = 0;
297bbdb7433SSean Christopherson 	asm("callq *%1" : "+m"(*mem) : "r"(insn_ram));
298bbdb7433SSean Christopherson 	report(*mem == 0x10000, "movb $imm, 0(%%rip)");
299bbdb7433SSean Christopherson }
300bbdb7433SSean Christopherson 
301bbdb7433SSean Christopherson static void test_cmov(u32 *mem)
302bbdb7433SSean Christopherson {
303bbdb7433SSean Christopherson 	u64 val;
304bbdb7433SSean Christopherson 	*mem = 0xabcdef12u;
305bbdb7433SSean Christopherson 	asm ("movq $0x1234567812345678, %%rax\n\t"
306bbdb7433SSean Christopherson 	     "cmpl %%eax, %%eax\n\t"
307bbdb7433SSean Christopherson 	     "cmovnel (%[mem]), %%eax\n\t"
308bbdb7433SSean Christopherson 	     "movq %%rax, %[val]\n\t"
309bbdb7433SSean Christopherson 	     : [val]"=r"(val) : [mem]"r"(mem) : "%rax", "cc");
310bbdb7433SSean Christopherson 	report(val == 0x12345678ul, "cmovnel");
311bbdb7433SSean Christopherson }
312bbdb7433SSean Christopherson 
313bbdb7433SSean Christopherson 
314bbdb7433SSean Christopherson static void test_mmx_movq_mf(uint64_t *mem)
315bbdb7433SSean Christopherson {
316bbdb7433SSean Christopherson 	/* movq %mm0, (%rax) */
317bbdb7433SSean Christopherson 	extern char movq_start, movq_end;
318bbdb7433SSean Christopherson 	handler old;
319bbdb7433SSean Christopherson 
320bbdb7433SSean Christopherson 	uint16_t fcw = 0;  /* all exceptions unmasked */
321bbdb7433SSean Christopherson 	write_cr0(read_cr0() & ~6);  /* TS, EM */
322bbdb7433SSean Christopherson 	exceptions = 0;
323bbdb7433SSean Christopherson 	old = handle_exception(MF_VECTOR, advance_rip_and_note_exception);
324bbdb7433SSean Christopherson 	asm volatile("fninit; fldcw %0" : : "m"(fcw));
325bbdb7433SSean Christopherson 	asm volatile("fldz; fldz; fdivp"); /* generate exception */
326bbdb7433SSean Christopherson 
327bbdb7433SSean Christopherson 	rip_advance = &movq_end - &movq_start;
328bbdb7433SSean Christopherson 	asm(KVM_FEP "movq_start: movq %mm0, (%rax); movq_end:");
329bbdb7433SSean Christopherson 	/* exit MMX mode */
330bbdb7433SSean Christopherson 	asm volatile("fnclex; emms");
331bbdb7433SSean Christopherson 	report(exceptions == 1, "movq mmx generates #MF");
332bbdb7433SSean Christopherson 	handle_exception(MF_VECTOR, old);
333bbdb7433SSean Christopherson }
334bbdb7433SSean Christopherson 
335bbdb7433SSean Christopherson static void test_jmp_noncanonical(uint64_t *mem)
336bbdb7433SSean Christopherson {
337bbdb7433SSean Christopherson 	extern char nc_jmp_start, nc_jmp_end;
338bbdb7433SSean Christopherson 	handler old;
339bbdb7433SSean Christopherson 
340bbdb7433SSean Christopherson 	*mem = 0x1111111111111111ul;
341bbdb7433SSean Christopherson 
342bbdb7433SSean Christopherson 	exceptions = 0;
343bbdb7433SSean Christopherson 	rip_advance = &nc_jmp_end - &nc_jmp_start;
344bbdb7433SSean Christopherson 	old = handle_exception(GP_VECTOR, advance_rip_and_note_exception);
345bbdb7433SSean Christopherson 	asm volatile ("nc_jmp_start: jmp *%0; nc_jmp_end:" : : "m"(*mem));
346bbdb7433SSean Christopherson 	report(exceptions == 1, "jump to non-canonical address");
347bbdb7433SSean Christopherson 	handle_exception(GP_VECTOR, old);
348bbdb7433SSean Christopherson }
349bbdb7433SSean Christopherson 
350bbdb7433SSean Christopherson static void test_movabs(uint64_t *mem)
351bbdb7433SSean Christopherson {
352bbdb7433SSean Christopherson 	/* mov $0x9090909090909090, %rcx */
353bbdb7433SSean Christopherson 	unsigned long rcx;
354bbdb7433SSean Christopherson 	asm(KVM_FEP "mov $0x9090909090909090, %0" : "=c" (rcx) : "0" (0));
355bbdb7433SSean Christopherson 	report(rcx == 0x9090909090909090, "64-bit mov imm2");
356bbdb7433SSean Christopherson }
357bbdb7433SSean Christopherson 
35805b0460eSMichal Luczaj static void load_dpl0_seg(void)
35905b0460eSMichal Luczaj {
36005b0460eSMichal Luczaj 	asm volatile(KVM_FEP "mov %0, %%fs" :: "r" (KERNEL_CS)); /* RPL=0 */
36105b0460eSMichal Luczaj }
36205b0460eSMichal Luczaj 
36305b0460eSMichal Luczaj static void test_user_load_dpl0_seg(void)
36405b0460eSMichal Luczaj {
36505b0460eSMichal Luczaj 	bool raised_vector;
36605b0460eSMichal Luczaj 
36705b0460eSMichal Luczaj 	run_in_user((usermode_func)load_dpl0_seg, GP_VECTOR, 0, 0, 0, 0,
36805b0460eSMichal Luczaj 		    &raised_vector);
36905b0460eSMichal Luczaj 
37005b0460eSMichal Luczaj 	report(raised_vector, "Wanted #GP on CPL=3 DPL=0 segment load");
37105b0460eSMichal Luczaj }
37205b0460eSMichal Luczaj 
373bbdb7433SSean Christopherson static void test_push16(uint64_t *mem)
374bbdb7433SSean Christopherson {
375bbdb7433SSean Christopherson 	uint64_t rsp1, rsp2;
376bbdb7433SSean Christopherson 	uint16_t r;
377bbdb7433SSean Christopherson 
378bbdb7433SSean Christopherson 	asm volatile (	"movq %%rsp, %[rsp1]\n\t"
379bbdb7433SSean Christopherson 			"pushw %[v]\n\t"
380bbdb7433SSean Christopherson 			"popw %[r]\n\t"
381bbdb7433SSean Christopherson 			"movq %%rsp, %[rsp2]\n\t"
382bbdb7433SSean Christopherson 			"movq %[rsp1], %%rsp\n\t" :
383bbdb7433SSean Christopherson 			[rsp1]"=r"(rsp1), [rsp2]"=r"(rsp2), [r]"=r"(r)
384bbdb7433SSean Christopherson 			: [v]"m"(*mem) : "memory");
385bbdb7433SSean Christopherson 	report(rsp1 == rsp2, "push16");
386bbdb7433SSean Christopherson }
387bbdb7433SSean Christopherson 
388bbdb7433SSean Christopherson static void test_sreg(volatile uint16_t *mem)
389bbdb7433SSean Christopherson {
390bbdb7433SSean Christopherson 	u16 ss = read_ss();
391bbdb7433SSean Christopherson 
392bbdb7433SSean Christopherson 	// check for null segment load
393bbdb7433SSean Christopherson 	*mem = 0;
394bbdb7433SSean Christopherson 	asm volatile("mov %0, %%ss" : : "m"(*mem));
395bbdb7433SSean Christopherson 	report(read_ss() == 0, "mov null, %%ss");
396bbdb7433SSean Christopherson 
397bbdb7433SSean Christopherson 	// check for exception when ss.rpl != cpl on null segment load
398bbdb7433SSean Christopherson 	*mem = 3;
399369432acSMathias Krause 	asm volatile(ASM_TRY("1f") "mov %0, %%ss; 1:" : : "m"(*mem));
400369432acSMathias Krause 	report(exception_vector() == GP_VECTOR &&
401369432acSMathias Krause 	       exception_error_code() == 0 && read_ss() == 0,
402bbdb7433SSean Christopherson 	       "mov null, %%ss (with ss.rpl != cpl)");
403369432acSMathias Krause 
404*cb0fabbbSMathias Krause 	// check for exception when ss.rpl != cpl on non-null segment load
405*cb0fabbbSMathias Krause 	*mem = KERNEL_DS | 3;
406*cb0fabbbSMathias Krause 	asm volatile(ASM_TRY("1f") "mov %0, %%ss; 1:" : : "m"(*mem));
407*cb0fabbbSMathias Krause 	report(exception_vector() == GP_VECTOR &&
408*cb0fabbbSMathias Krause 	       exception_error_code() == KERNEL_DS && read_ss() == 0,
409*cb0fabbbSMathias Krause 	       "mov non-null, %%ss (with ss.rpl != cpl)");
410*cb0fabbbSMathias Krause 
411bbdb7433SSean Christopherson 	write_ss(ss);
412bbdb7433SSean Christopherson }
413bbdb7433SSean Christopherson 
414bbdb7433SSean Christopherson static uint64_t usr_gs_mov(void)
415bbdb7433SSean Christopherson {
416bbdb7433SSean Christopherson 	static uint64_t dummy = MAGIC_NUM;
417bbdb7433SSean Christopherson 	uint64_t dummy_ptr = (uint64_t)&dummy;
418bbdb7433SSean Christopherson 	uint64_t ret;
419bbdb7433SSean Christopherson 
420bbdb7433SSean Christopherson 	dummy_ptr -= GS_BASE;
4214a7802f3SMathias Krause 	asm volatile("mov %%gs:(%1), %0" : "=r"(ret) : "r"(dummy_ptr));
422bbdb7433SSean Christopherson 
423bbdb7433SSean Christopherson 	return ret;
424bbdb7433SSean Christopherson }
425bbdb7433SSean Christopherson 
426bbdb7433SSean Christopherson static void test_iret(void)
427bbdb7433SSean Christopherson {
428bbdb7433SSean Christopherson 	uint64_t val;
429bbdb7433SSean Christopherson 	bool raised_vector;
430bbdb7433SSean Christopherson 
431bbdb7433SSean Christopherson 	/* Update GS base to 4MiB */
432bbdb7433SSean Christopherson 	wrmsr(MSR_GS_BASE, GS_BASE);
433bbdb7433SSean Christopherson 
434bbdb7433SSean Christopherson 	/*
435bbdb7433SSean Christopherson 	* Per the SDM, jumping to user mode via `iret`, which is returning to
436bbdb7433SSean Christopherson 	* outer privilege level, for segment registers (ES, FS, GS, and DS)
437bbdb7433SSean Christopherson 	* if the check fails, the segment selector becomes null.
438bbdb7433SSean Christopherson 	*
439bbdb7433SSean Christopherson 	* In our test case, GS becomes null.
440bbdb7433SSean Christopherson 	*/
441bbdb7433SSean Christopherson 	val = run_in_user((usermode_func)usr_gs_mov, GP_VECTOR,
442bbdb7433SSean Christopherson 			0, 0, 0, 0, &raised_vector);
443bbdb7433SSean Christopherson 
444bbdb7433SSean Christopherson 	report(val == MAGIC_NUM, "Test ret/iret with a nullified segment");
445bbdb7433SSean Christopherson }
446bbdb7433SSean Christopherson 
447bbdb7433SSean Christopherson static void test_emulator_64(void *mem)
448bbdb7433SSean Christopherson {
449bbdb7433SSean Christopherson 	void *insn_page = alloc_page();
450bbdb7433SSean Christopherson 	void *insn_ram  = vmap(virt_to_phys(insn_page), 4096);
451bbdb7433SSean Christopherson 
452bbdb7433SSean Christopherson 	test_push(mem);
453bbdb7433SSean Christopherson 	test_pop(mem);
454bbdb7433SSean Christopherson 
455bbdb7433SSean Christopherson 	test_xchg(mem);
456bbdb7433SSean Christopherson 	test_xadd(mem);
457bbdb7433SSean Christopherson 
458bbdb7433SSean Christopherson 	test_cr8();
459bbdb7433SSean Christopherson 
460bbdb7433SSean Christopherson 	test_ljmp(mem);
461bbdb7433SSean Christopherson 	test_muldiv(mem);
462bbdb7433SSean Christopherson 	test_mmx(mem);
463bbdb7433SSean Christopherson 	test_rip_relative(mem, insn_ram);
464bbdb7433SSean Christopherson 	test_iret();
465bbdb7433SSean Christopherson 	test_sreg(mem);
466bbdb7433SSean Christopherson 	test_cmov(mem);
467bbdb7433SSean Christopherson 
468bbdb7433SSean Christopherson 	if (is_fep_available()) {
469bbdb7433SSean Christopherson 		test_mmx_movq_mf(mem);
470bbdb7433SSean Christopherson 		test_movabs(mem);
47105b0460eSMichal Luczaj 		test_user_load_dpl0_seg();
472bbdb7433SSean Christopherson 	}
473bbdb7433SSean Christopherson 
474bbdb7433SSean Christopherson 	test_push16(mem);
475bbdb7433SSean Christopherson 
476bbdb7433SSean Christopherson 	test_jmp_noncanonical(mem);
477bbdb7433SSean Christopherson }
478