xref: /linux/tools/testing/selftests/kvm/x86/fastops_test.c (revision 9738280aae592b579a25b5b1b6584c894827d3c7)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include "test_util.h"
3 #include "kvm_util.h"
4 #include "processor.h"
5 
6 /*
7  * Execute a fastop() instruction, with or without forced emulation.  BT bit 0
8  * to set RFLAGS.CF based on whether or not the input is even or odd, so that
9  * instructions like ADC and SBB are deterministic.
10  */
11 #define guest_execute_fastop_1(FEP, insn, __val, __flags)				\
12 ({											\
13 	__asm__ __volatile__("bt $0, %[val]\n\t"					\
14 			     FEP insn " %[val]\n\t"					\
15 			     "pushfq\n\t"						\
16 			     "pop %[flags]\n\t"						\
17 			     : [val]"+r"(__val), [flags]"=r"(__flags)			\
18 			     : : "cc", "memory");					\
19 })
20 
21 #define guest_test_fastop_1(insn, type_t, __val)					\
22 ({											\
23 	type_t val = __val, ex_val = __val, input = __val;				\
24 	uint64_t flags, ex_flags;							\
25 											\
26 	guest_execute_fastop_1("", insn, ex_val, ex_flags);				\
27 	guest_execute_fastop_1(KVM_FEP, insn, val, flags);				\
28 											\
29 	__GUEST_ASSERT(val == ex_val,							\
30 		       "Wanted 0x%lx for '%s 0x%lx', got 0x%lx",			\
31 		       (uint64_t)ex_val, insn, (uint64_t)input, (uint64_t)val);		\
32 	__GUEST_ASSERT(flags == ex_flags,						\
33 			"Wanted flags 0x%lx for '%s 0x%lx', got 0x%lx",			\
34 			ex_flags, insn, (uint64_t)input, flags);			\
35 })
36 
37 #define guest_execute_fastop_2(FEP, insn, __input, __output, __flags)			\
38 ({											\
39 	__asm__ __volatile__("bt $0, %[output]\n\t"					\
40 			     FEP insn " %[input], %[output]\n\t"			\
41 			     "pushfq\n\t"						\
42 			     "pop %[flags]\n\t"						\
43 			     : [output]"+r"(__output), [flags]"=r"(__flags)		\
44 			     : [input]"r"(__input) : "cc", "memory");			\
45 })
46 
47 #define guest_test_fastop_2(insn, type_t, __val1, __val2)				\
48 ({											\
49 	type_t input = __val1, input2 = __val2, output = __val2, ex_output = __val2;	\
50 	uint64_t flags, ex_flags;							\
51 											\
52 	guest_execute_fastop_2("", insn, input, ex_output, ex_flags);			\
53 	guest_execute_fastop_2(KVM_FEP, insn, input, output, flags);			\
54 											\
55 	__GUEST_ASSERT(output == ex_output,						\
56 		       "Wanted 0x%lx for '%s 0x%lx 0x%lx', got 0x%lx",			\
57 		       (uint64_t)ex_output, insn, (uint64_t)input,			\
58 		       (uint64_t)input2, (uint64_t)output);				\
59 	__GUEST_ASSERT(flags == ex_flags,						\
60 			"Wanted flags 0x%lx for '%s 0x%lx, 0x%lx', got 0x%lx",		\
61 			ex_flags, insn, (uint64_t)input, (uint64_t)input2, flags);	\
62 })
63 
64 #define guest_execute_fastop_cl(FEP, insn, __shift, __output, __flags)			\
65 ({											\
66 	__asm__ __volatile__("bt $0, %[output]\n\t"					\
67 			     FEP insn " %%cl, %[output]\n\t"				\
68 			     "pushfq\n\t"						\
69 			     "pop %[flags]\n\t"						\
70 			     : [output]"+r"(__output), [flags]"=r"(__flags)		\
71 			     : "c"(__shift) : "cc", "memory");				\
72 })
73 
74 #define guest_test_fastop_cl(insn, type_t, __val1, __val2)				\
75 ({											\
76 	type_t output = __val2, ex_output = __val2, input = __val2;			\
77 	uint8_t shift = __val1;								\
78 	uint64_t flags, ex_flags;							\
79 											\
80 	guest_execute_fastop_cl("", insn, shift, ex_output, ex_flags);			\
81 	guest_execute_fastop_cl(KVM_FEP, insn, shift, output, flags);			\
82 											\
83 	__GUEST_ASSERT(output == ex_output,						\
84 		       "Wanted 0x%lx for '%s 0x%x, 0x%lx', got 0x%lx",			\
85 		       (uint64_t)ex_output, insn, shift, (uint64_t)input,		\
86 		       (uint64_t)output);						\
87 	__GUEST_ASSERT(flags == ex_flags,						\
88 			"Wanted flags 0x%lx for '%s 0x%x, 0x%lx', got 0x%lx",		\
89 			ex_flags, insn, shift, (uint64_t)input, flags);			\
90 })
91 
92 static const uint64_t vals[] = {
93 	0,
94 	1,
95 	2,
96 	4,
97 	7,
98 	0x5555555555555555,
99 	0xaaaaaaaaaaaaaaaa,
100 	0xfefefefefefefefe,
101 	0xffffffffffffffff,
102 };
103 
104 #define guest_test_fastops(type_t, suffix)						\
105 do {											\
106 	int i, j;									\
107 											\
108 	for (i = 0; i < ARRAY_SIZE(vals); i++) {					\
109 		guest_test_fastop_1("dec" suffix, type_t, vals[i]);			\
110 		guest_test_fastop_1("inc" suffix, type_t, vals[i]);			\
111 		guest_test_fastop_1("neg" suffix, type_t, vals[i]);			\
112 		guest_test_fastop_1("not" suffix, type_t, vals[i]);			\
113 											\
114 		for (j = 0; j < ARRAY_SIZE(vals); j++) {				\
115 			guest_test_fastop_2("add" suffix, type_t, vals[i], vals[j]);	\
116 			guest_test_fastop_2("adc" suffix, type_t, vals[i], vals[j]);	\
117 			guest_test_fastop_2("and" suffix, type_t, vals[i], vals[j]);	\
118 			guest_test_fastop_2("bsf" suffix, type_t, vals[i], vals[j]);	\
119 			guest_test_fastop_2("bsr" suffix, type_t, vals[i], vals[j]);	\
120 			guest_test_fastop_2("bt" suffix, type_t, vals[i], vals[j]);	\
121 			guest_test_fastop_2("btc" suffix, type_t, vals[i], vals[j]);	\
122 			guest_test_fastop_2("btr" suffix, type_t, vals[i], vals[j]);	\
123 			guest_test_fastop_2("bts" suffix, type_t, vals[i], vals[j]);	\
124 			guest_test_fastop_2("cmp" suffix, type_t, vals[i], vals[j]);	\
125 			guest_test_fastop_2("imul" suffix, type_t, vals[i], vals[j]);	\
126 			guest_test_fastop_2("or" suffix, type_t, vals[i], vals[j]);	\
127 			guest_test_fastop_2("sbb" suffix, type_t, vals[i], vals[j]);	\
128 			guest_test_fastop_2("sub" suffix, type_t, vals[i], vals[j]);	\
129 			guest_test_fastop_2("test" suffix, type_t, vals[i], vals[j]);	\
130 			guest_test_fastop_2("xor" suffix, type_t, vals[i], vals[j]);	\
131 											\
132 			guest_test_fastop_cl("rol" suffix, type_t, vals[i], vals[j]);	\
133 			guest_test_fastop_cl("ror" suffix, type_t, vals[i], vals[j]);	\
134 			guest_test_fastop_cl("rcl" suffix, type_t, vals[i], vals[j]);	\
135 			guest_test_fastop_cl("rcr" suffix, type_t, vals[i], vals[j]);	\
136 			guest_test_fastop_cl("sar" suffix, type_t, vals[i], vals[j]);	\
137 			guest_test_fastop_cl("shl" suffix, type_t, vals[i], vals[j]);	\
138 			guest_test_fastop_cl("shr" suffix, type_t, vals[i], vals[j]);	\
139 		}									\
140 	}										\
141 } while (0)
142 
143 static void guest_code(void)
144 {
145 	guest_test_fastops(uint16_t, "w");
146 	guest_test_fastops(uint32_t, "l");
147 	guest_test_fastops(uint64_t, "q");
148 
149 	GUEST_DONE();
150 }
151 
152 int main(int argc, char *argv[])
153 {
154 	struct kvm_vcpu *vcpu;
155 	struct kvm_vm *vm;
156 
157 	TEST_REQUIRE(is_forced_emulation_enabled);
158 
159 	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
160 
161 	vcpu_run(vcpu);
162 	TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE);
163 
164 	kvm_vm_free(vm);
165 }
166