xref: /kvm-unit-tests/x86/emulator.c (revision 7d36db351752e29ad27eaafe3f102de7064e429b)
1 #include "ioram.h"
2 #include "vm.h"
3 #include "libcflat.h"
4 
5 #define memset __builtin_memset
6 #define TESTDEV_IO_PORT 0xe0
7 
8 int fails, tests;
9 
10 void report(const char *name, int result)
11 {
12 	++tests;
13 	if (result)
14 		printf("PASS: %s\n", name);
15 	else {
16 		printf("FAIL: %s\n", name);
17 		++fails;
18 	}
19 }
20 
21 static char st1[] = "abcdefghijklmnop";
22 
23 void test_stringio()
24 {
25 	unsigned char r = 0;
26 	asm volatile("cld \n\t"
27 		     "movw %0, %%dx \n\t"
28 		     "rep outsb \n\t"
29 		     : : "i"((short)TESTDEV_IO_PORT),
30 		       "S"(st1), "c"(sizeof(st1) - 1));
31 	asm volatile("inb %1, %0\n\t" : "=a"(r) : "i"((short)TESTDEV_IO_PORT));
32 	report("outsb up", r == st1[sizeof(st1) - 2]); /* last char */
33 
34 	asm volatile("std \n\t"
35 		     "movw %0, %%dx \n\t"
36 		     "rep outsb \n\t"
37 		     : : "i"((short)TESTDEV_IO_PORT),
38 		       "S"(st1 + sizeof(st1) - 2), "c"(sizeof(st1) - 1));
39 	asm volatile("cld \n\t" : : );
40 	asm volatile("in %1, %0\n\t" : "=a"(r) : "i"((short)TESTDEV_IO_PORT));
41 	report("outsb down", r == st1[0]);
42 }
43 
44 void test_cmps_one(unsigned char *m1, unsigned char *m3)
45 {
46 	void *rsi, *rdi;
47 	long rcx, tmp;
48 
49 	rsi = m1; rdi = m3; rcx = 30;
50 	asm volatile("xor %[tmp], %[tmp] \n\t"
51 		     "repe/cmpsb"
52 		     : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
53 		     : : "cc");
54 	report("repe/cmpsb (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30);
55 
56 	rsi = m1; rdi = m3; rcx = 15;
57 	asm volatile("xor %[tmp], %[tmp] \n\t"
58 		     "repe/cmpsw"
59 		     : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
60 		     : : "cc");
61 	report("repe/cmpsw (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30);
62 
63 	rsi = m1; rdi = m3; rcx = 7;
64 	asm volatile("xor %[tmp], %[tmp] \n\t"
65 		     "repe/cmpsl"
66 		     : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
67 		     : : "cc");
68 	report("repe/cmpll (1)", rcx == 0 && rsi == m1 + 28 && rdi == m3 + 28);
69 
70 	rsi = m1; rdi = m3; rcx = 4;
71 	asm volatile("xor %[tmp], %[tmp] \n\t"
72 		     "repe/cmpsq"
73 		     : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
74 		     : : "cc");
75 	report("repe/cmpsq (1)", rcx == 0 && rsi == m1 + 32 && rdi == m3 + 32);
76 
77 	rsi = m1; rdi = m3; rcx = 130;
78 	asm volatile("xor %[tmp], %[tmp] \n\t"
79 		     "repe/cmpsb"
80 		     : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
81 		     : : "cc");
82 	report("repe/cmpsb (2)",
83 	       rcx == 29 && rsi == m1 + 101 && rdi == m3 + 101);
84 
85 	rsi = m1; rdi = m3; rcx = 65;
86 	asm volatile("xor %[tmp], %[tmp] \n\t"
87 		     "repe/cmpsw"
88 		     : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
89 		     : : "cc");
90 	report("repe/cmpsw (2)",
91 	       rcx == 14 && rsi == m1 + 102 && rdi == m3 + 102);
92 
93 	rsi = m1; rdi = m3; rcx = 32;
94 	asm volatile("xor %[tmp], %[tmp] \n\t"
95 		     "repe/cmpsl"
96 		     : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
97 		     : : "cc");
98 	report("repe/cmpll (2)",
99 	       rcx == 6 && rsi == m1 + 104 && rdi == m3 + 104);
100 
101 	rsi = m1; rdi = m3; rcx = 16;
102 	asm volatile("xor %[tmp], %[tmp] \n\t"
103 		     "repe/cmpsq"
104 		     : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
105 		     : : "cc");
106 	report("repe/cmpsq (2)",
107 	       rcx == 3 && rsi == m1 + 104 && rdi == m3 + 104);
108 
109 }
110 
111 void test_cmps(void *mem)
112 {
113 	unsigned char *m1 = mem, *m2 = mem + 1024;
114 	unsigned char m3[1024];
115 
116 	for (int i = 0; i < 100; ++i)
117 		m1[i] = m2[i] = m3[i] = i;
118 	for (int i = 100; i < 200; ++i)
119 		m1[i] = (m3[i] = m2[i] = i) + 1;
120 	test_cmps_one(m1, m3);
121 	test_cmps_one(m1, m2);
122 }
123 
124 void test_cr8(void)
125 {
126 	unsigned long src, dst;
127 
128 	dst = 777;
129 	src = 3;
130 	asm volatile("mov %[src], %%cr8; mov %%cr8, %[dst]"
131 		     : [dst]"+r"(dst), [src]"+r"(src));
132 	report("mov %cr8", dst == 3 && src == 3);
133 }
134 
135 void test_push(void *mem)
136 {
137 	unsigned long tmp;
138 	unsigned long *stack_top = mem + 4096;
139 	unsigned long *new_stack_top;
140 	unsigned long memw = 0x123456789abcdeful;
141 
142 	memset(mem, 0x55, (void *)stack_top - mem);
143 
144 	asm volatile("mov %%rsp, %[tmp] \n\t"
145 		     "mov %[stack_top], %%rsp \n\t"
146 		     "pushq $-7 \n\t"
147 		     "pushq %[reg] \n\t"
148 		     "pushq (%[mem]) \n\t"
149 		     "pushq $-7070707 \n\t"
150 		     "mov %%rsp, %[new_stack_top] \n\t"
151 		     "mov %[tmp], %%rsp"
152 		     : [tmp]"=&r"(tmp), [new_stack_top]"=r"(new_stack_top)
153 		     : [stack_top]"r"(stack_top),
154 		       [reg]"r"(-17l), [mem]"r"(&memw)
155 		     : "memory");
156 
157 	report("push $imm8", stack_top[-1] == -7ul);
158 	report("push %reg", stack_top[-2] == -17ul);
159 	report("push mem", stack_top[-3] == 0x123456789abcdeful);
160 	report("push $imm", stack_top[-4] == -7070707);
161 }
162 
163 void test_pop(void *mem)
164 {
165 	unsigned long tmp;
166 	unsigned long *stack_top = mem + 4096;
167 	unsigned long memw = 0x123456789abcdeful;
168 	static unsigned long tmp2;
169 
170 	memset(mem, 0x55, (void *)stack_top - mem);
171 
172 	asm volatile("pushq %[val] \n\t"
173 		     "popq (%[mem])"
174 		     : : [val]"m"(memw), [mem]"r"(mem) : "memory");
175 	report("pop mem", *(unsigned long *)mem == memw);
176 
177 	memw = 7 - memw;
178 	asm volatile("mov %%rsp, %[tmp] \n\t"
179 		     "mov %[stack_top], %%rsp \n\t"
180 		     "pushq %[val] \n\t"
181 		     "popq %[tmp2] \n\t"
182 		     "mov %[tmp], %%rsp"
183 		     : [tmp]"=&r"(tmp), [tmp2]"=m"(tmp2)
184 		     : [val]"r"(memw), [stack_top]"r"(stack_top)
185 		     : "memory");
186 	report("pop mem (2)", tmp2 == memw);
187 
188 	memw = 129443 - memw;
189 	asm volatile("mov %%rsp, %[tmp] \n\t"
190 		     "mov %[stack_top], %%rsp \n\t"
191 		     "pushq %[val] \n\t"
192 		     "popq %[tmp2] \n\t"
193 		     "mov %[tmp], %%rsp"
194 		     : [tmp]"=&r"(tmp), [tmp2]"=r"(tmp2)
195 		     : [val]"r"(memw), [stack_top]"r"(stack_top)
196 		     : "memory");
197 	report("pop reg", tmp2 == memw);
198 
199 	asm volatile("mov %%rsp, %[tmp] \n\t"
200 		     "mov %[stack_top], %%rsp \n\t"
201 		     "push $1f \n\t"
202 		     "ret \n\t"
203 		     "2: jmp 2b \n\t"
204 		     "1: mov %[tmp], %%rsp"
205 		     : [tmp]"=&r"(tmp) : [stack_top]"r"(stack_top)
206 		     : "memory");
207 	report("ret", 1);
208 }
209 
210 void test_ljmp(void *mem)
211 {
212     unsigned char *m = mem;
213     volatile int res = 1;
214 
215     *(unsigned long**)m = &&jmpf;
216     asm volatile ("data16/mov %%cs, %0":"=m"(*(m + sizeof(unsigned long))));
217     asm volatile ("rex64/ljmp *%0"::"m"(*m));
218     res = 0;
219 jmpf:
220     report("ljmp", res);
221 }
222 
223 void test_incdecnotneg(void *mem)
224 {
225     unsigned long *m = mem, v = 1234;
226     unsigned char *mb = mem, vb = 66;
227 
228     *m = 0;
229 
230     asm volatile ("incl %0":"+m"(*m));
231     report("incl",  *m == 1);
232     asm volatile ("decl %0":"+m"(*m));
233     report("decl",  *m == 0);
234     asm volatile ("incb %0":"+m"(*m));
235     report("incb",  *m == 1);
236     asm volatile ("decb %0":"+m"(*m));
237     report("decb",  *m == 0);
238 
239     asm volatile ("lock incl %0":"+m"(*m));
240     report("lock incl",  *m == 1);
241     asm volatile ("lock decl %0":"+m"(*m));
242     report("lock decl",  *m == 0);
243     asm volatile ("lock incb %0":"+m"(*m));
244     report("lock incb",  *m == 1);
245     asm volatile ("lock decb %0":"+m"(*m));
246     report("lock decb",  *m == 0);
247 
248     *m = v;
249 
250     asm ("lock negq %0" : "+m"(*m)); v = -v;
251     report("lock negl", *m == v);
252     asm ("lock notq %0" : "+m"(*m)); v = ~v;
253     report("lock notl", *m == v);
254 
255     *mb = vb;
256 
257     asm ("lock negb %0" : "+m"(*mb)); vb = -vb;
258     report("lock negb", *mb == vb);
259     asm ("lock notb %0" : "+m"(*mb)); vb = ~vb;
260     report("lock notb", *mb == vb);
261 }
262 
263 void test_smsw(void)
264 {
265 	char mem[16];
266 	unsigned short msw, msw_orig, *pmsw;
267 	int i, zero;
268 
269 	msw_orig = read_cr0();
270 
271 	asm("smsw %0" : "=r"(msw));
272 	report("smsw (1)", msw == msw_orig);
273 
274 	memset(mem, 0, 16);
275 	pmsw = (void *)mem;
276 	asm("smsw %0" : "=m"(pmsw[4]));
277 	zero = 1;
278 	for (i = 0; i < 8; ++i)
279 		if (i != 4 && pmsw[i])
280 			zero = 0;
281 	report("smsw (2)", msw == pmsw[4] && zero);
282 }
283 
284 void test_lmsw(void)
285 {
286 	char mem[16];
287 	unsigned short msw, *pmsw;
288 	unsigned long cr0;
289 
290 	cr0 = read_cr0();
291 
292 	msw = cr0 ^ 8;
293 	asm("lmsw %0" : : "r"(msw));
294 	printf("before %lx after %lx\n", cr0, read_cr0());
295 	report("lmsw (1)", (cr0 ^ read_cr0()) == 8);
296 
297 	pmsw = (void *)mem;
298 	*pmsw = cr0;
299 	asm("lmsw %0" : : "m"(*pmsw));
300 	printf("before %lx after %lx\n", cr0, read_cr0());
301 	report("lmsw (2)", cr0 == read_cr0());
302 
303 	/* lmsw can't clear cr0.pe */
304 	msw = (cr0 & ~1ul) ^ 4;  /* change EM to force trap */
305 	asm("lmsw %0" : : "r"(msw));
306 	report("lmsw (3)", (cr0 ^ read_cr0()) == 4 && (cr0 & 1));
307 
308 	/* back to normal */
309 	msw = cr0;
310 	asm("lmsw %0" : : "r"(msw));
311 }
312 
313 void test_xchg(void *mem)
314 {
315 	unsigned long *memq = mem;
316 	unsigned long rax;
317 
318 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
319 		     "mov %%rax, (%[memq])\n\t"
320 		     "mov $0xfedcba9876543210, %%rax\n\t"
321 		     "xchg %%al, (%[memq])\n\t"
322 		     "mov %%rax, %[rax]\n\t"
323 		     : [rax]"=r"(rax)
324 		     : [memq]"r"(memq)
325 		     : "memory");
326 	report("xchg reg, r/m (1)",
327 	       rax == 0xfedcba98765432ef && *memq == 0x123456789abcd10);
328 
329 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
330 		     "mov %%rax, (%[memq])\n\t"
331 		     "mov $0xfedcba9876543210, %%rax\n\t"
332 		     "xchg %%ax, (%[memq])\n\t"
333 		     "mov %%rax, %[rax]\n\t"
334 		     : [rax]"=r"(rax)
335 		     : [memq]"r"(memq)
336 		     : "memory");
337 	report("xchg reg, r/m (2)",
338 	       rax == 0xfedcba987654cdef && *memq == 0x123456789ab3210);
339 
340 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
341 		     "mov %%rax, (%[memq])\n\t"
342 		     "mov $0xfedcba9876543210, %%rax\n\t"
343 		     "xchg %%eax, (%[memq])\n\t"
344 		     "mov %%rax, %[rax]\n\t"
345 		     : [rax]"=r"(rax)
346 		     : [memq]"r"(memq)
347 		     : "memory");
348 	report("xchg reg, r/m (3)",
349 	       rax == 0x89abcdef && *memq == 0x123456776543210);
350 
351 	asm volatile("mov $0x123456789abcdef, %%rax\n\t"
352 		     "mov %%rax, (%[memq])\n\t"
353 		     "mov $0xfedcba9876543210, %%rax\n\t"
354 		     "xchg %%rax, (%[memq])\n\t"
355 		     "mov %%rax, %[rax]\n\t"
356 		     : [rax]"=r"(rax)
357 		     : [memq]"r"(memq)
358 		     : "memory");
359 	report("xchg reg, r/m (4)",
360 	       rax == 0x123456789abcdef && *memq == 0xfedcba9876543210);
361 }
362 
363 int main()
364 {
365 	void *mem;
366 	unsigned long t1, t2;
367 
368 	setup_vm();
369 	mem = vmap(IORAM_BASE_PHYS, IORAM_LEN);
370 
371 	// test mov reg, r/m and mov r/m, reg
372 	t1 = 0x123456789abcdef;
373 	asm volatile("mov %[t1], (%[mem]) \n\t"
374 		     "mov (%[mem]), %[t2]"
375 		     : [t2]"=r"(t2)
376 		     : [t1]"r"(t1), [mem]"r"(mem)
377 		     : "memory");
378 	report("mov reg, r/m (1)", t2 == 0x123456789abcdef);
379 
380 	test_cmps(mem);
381 
382 	test_push(mem);
383 	test_pop(mem);
384 
385 	test_xchg(mem);
386 
387 	test_cr8();
388 
389 	test_smsw();
390 	test_lmsw();
391 	test_ljmp(mem);
392 	test_stringio();
393 	test_incdecnotneg(mem);
394 
395 	printf("\nSUMMARY: %d tests, %d failures\n", tests, fails);
396 	return fails ? 1 : 0;
397 }
398