1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "gtest/gtest.h"
18
19 #include "berberis/assembler/x86_64.h"
20
21 #include "berberis/assembler/machine_code.h"
22 #include "berberis/base/bit_util.h"
23 #include "berberis/code_gen_lib/gen_adaptor.h"
24 #include "berberis/code_gen_lib/gen_wrapper.h"
25 #include "berberis/guest_abi/guest_arguments.h"
26 #include "berberis/guest_state/guest_addr.h"
27 #include "berberis/guest_state/guest_state.h"
28 #include "berberis/runtime_primitives/host_code.h"
29 #include "berberis/runtime_primitives/translation_cache.h"
30 #include "berberis/test_utils/scoped_exec_region.h"
31 #include "berberis/test_utils/testing_run_generated_code.h"
32
33 namespace berberis {
34
35 namespace {
36
37 // Constant for NaN boxing and unboxing of 32-bit floats.
38 constexpr uint64_t kNanBoxFloat32 = 0xffff'ffff'0000'0000ULL;
39
40 bool g_called;
41 uint32_t g_arg;
42 ThreadState g_state{};
43 uint32_t g_insn;
44 uint32_t g_ret_insn;
45
DummyTrampoline(void * arg,ThreadState * state)46 void DummyTrampoline(void* arg, ThreadState* state) {
47 g_called = true;
48 ASSERT_EQ(&g_arg, arg);
49 ASSERT_EQ(&g_state, state);
50 ASSERT_EQ(g_state.cpu.insn_addr, ToGuestAddr(&g_insn));
51 ASSERT_EQ(GetLinkRegister(g_state.cpu), ToGuestAddr(&g_ret_insn));
52 }
53
TEST(CodeGenLib,GenTrampolineAdaptor)54 TEST(CodeGenLib, GenTrampolineAdaptor) {
55 MachineCode machine_code;
56
57 GenTrampolineAdaptor(
58 &machine_code, ToGuestAddr(&g_insn), AsHostCode(DummyTrampoline), &g_arg, "DummyTrampoline");
59
60 ScopedExecRegion exec(&machine_code);
61
62 g_called = false;
63 g_state.cpu.insn_addr = 0;
64 SetLinkRegister(g_state.cpu, ToGuestAddr(&g_ret_insn));
65
66 TestingRunGeneratedCode(&g_state, exec.get(), ToGuestAddr(&g_ret_insn));
67
68 ASSERT_TRUE(g_called);
69 ASSERT_EQ(g_state.cpu.insn_addr, ToGuestAddr(&g_ret_insn));
70 }
71
GenMoveResidenceToReg(MachineCode * machine_code)72 void GenMoveResidenceToReg(MachineCode* machine_code) {
73 x86_64::Assembler as(machine_code);
74 // Perform x0 = ThreadState::residence.
75 as.Movq(as.rdx, {.base = as.rbp, .disp = offsetof(ThreadState, residence)});
76 as.Movq({.base = as.rbp, .disp = offsetof(ThreadState, cpu.x[0])}, as.rdx);
77 as.Jmp(kEntryExitGeneratedCode);
78 }
79
GetResidenceReg(const ThreadState & state)80 uint64_t GetResidenceReg(const ThreadState& state) {
81 return state.cpu.x[0];
82 }
83
CheckResidenceTrampoline(void *,ThreadState * state)84 void CheckResidenceTrampoline(void*, ThreadState* state) {
85 EXPECT_EQ(state->residence, kOutsideGeneratedCode);
86 }
87
AddToTranslationCache(GuestAddr guest_addr,HostCodePiece host_code_piece)88 void AddToTranslationCache(GuestAddr guest_addr, HostCodePiece host_code_piece) {
89 auto* tc = TranslationCache::GetInstance();
90 GuestCodeEntry* entry = tc->AddAndLockForTranslation(guest_addr, 0);
91 ASSERT_NE(entry, nullptr);
92 tc->SetTranslatedAndUnlock(
93 guest_addr, entry, 1, GuestCodeEntry::Kind::kSpecialHandler, host_code_piece);
94 }
95
TEST(CodeGenLib,GenTrampolineAdaptorResidence)96 TEST(CodeGenLib, GenTrampolineAdaptorResidence) {
97 MachineCode trampoline_adaptor;
98 GenTrampolineAdaptor(&trampoline_adaptor,
99 ToGuestAddr(&g_insn),
100 AsHostCode(CheckResidenceTrampoline),
101 nullptr,
102 nullptr);
103 ScopedExecRegion trampoline_exec(&trampoline_adaptor);
104
105 // Trampoline returns to generated code, so we generate some.
106 MachineCode generated_code;
107 GenMoveResidenceToReg(&generated_code);
108 ScopedExecRegion generated_code_exec(&generated_code);
109
110 AddToTranslationCache(ToGuestAddr(&g_ret_insn),
111 {generated_code_exec.get(), generated_code.install_size()});
112
113 g_state.cpu.insn_addr = 0;
114 SetLinkRegister(g_state.cpu, ToGuestAddr(&g_ret_insn));
115 EXPECT_EQ(g_state.residence, kOutsideGeneratedCode);
116
117 berberis_RunGeneratedCode(&g_state, trampoline_exec.get());
118
119 EXPECT_EQ(g_state.residence, kOutsideGeneratedCode);
120 EXPECT_EQ(g_state.cpu.insn_addr, ToGuestAddr(&g_ret_insn));
121 EXPECT_EQ(GetResidenceReg(g_state), kInsideGeneratedCode);
122
123 TranslationCache::GetInstance()->InvalidateGuestRange(ToGuestAddr(&g_ret_insn),
124 ToGuestAddr(&g_ret_insn) + 1);
125 }
126
DummyRunner2(GuestAddr pc,GuestArgumentBuffer * buf)127 void DummyRunner2(GuestAddr pc, GuestArgumentBuffer* buf) {
128 g_called = true;
129 ASSERT_EQ(pc, ToGuestAddr(&g_insn));
130 ASSERT_NE(nullptr, buf);
131 ASSERT_EQ(1, buf->argc);
132 ASSERT_EQ(0, buf->resc);
133 ASSERT_EQ(1234u, buf->argv[0]);
134 }
135
TEST(CodeGenLib,GenWrapGuestFunction)136 TEST(CodeGenLib, GenWrapGuestFunction) {
137 MachineCode machine_code;
138
139 GenWrapGuestFunction(
140 &machine_code, ToGuestAddr(&g_insn), "vi", AsHostCode(DummyRunner2), "DummyRunner2");
141
142 ScopedExecRegion exec(&machine_code);
143
144 g_called = false;
145 exec.get<void(int)>()(1234);
146
147 ASSERT_TRUE(g_called);
148 }
149
Run10UInt8(GuestAddr pc,GuestArgumentBuffer * buf)150 void Run10UInt8(GuestAddr pc, GuestArgumentBuffer* buf) {
151 ASSERT_EQ(ToGuestAddr(&g_insn), pc);
152 ASSERT_NE(buf, nullptr);
153 ASSERT_EQ(buf->argc, 8);
154 ASSERT_EQ(buf->stack_argc, 16);
155 ASSERT_EQ(buf->resc, 1);
156 ASSERT_EQ(buf->argv[0], 0U);
157 ASSERT_EQ(buf->argv[1], 0xffU);
158 ASSERT_EQ(buf->argv[2], 2U);
159 ASSERT_EQ(buf->argv[3], 3U);
160 ASSERT_EQ(buf->argv[4], 4U);
161 ASSERT_EQ(buf->argv[5], 5U);
162 ASSERT_EQ(buf->argv[6], 6U);
163 ASSERT_EQ(buf->argv[7], 0xf9U);
164 ASSERT_EQ(buf->stack_argv[0], 0xf8U);
165 ASSERT_EQ(buf->stack_argv[1], 9U);
166 buf->argv[0] = 0xf6;
167 }
168
TEST(CodeGenLib,GenWrapGuestFunction_Run10UInt8)169 TEST(CodeGenLib, GenWrapGuestFunction_Run10UInt8) {
170 MachineCode machine_code;
171
172 GenWrapGuestFunction(
173 &machine_code, ToGuestAddr(&g_insn), "zzzzzzzzzzz", AsHostCode(Run10UInt8), "Run10UInt8");
174
175 ScopedExecRegion exec(&machine_code);
176
177 using Func = uint8_t(
178 uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
179 uint8_t res = exec.get<Func>()(0, 0xff, 2, 3, 4, 5, 6, 0xf9, 0xf8, 9);
180 ASSERT_EQ(res, 0xF6u);
181 }
182
Run10Int8(GuestAddr pc,GuestArgumentBuffer * buf)183 void Run10Int8(GuestAddr pc, GuestArgumentBuffer* buf) {
184 ASSERT_EQ(ToGuestAddr(&g_insn), pc);
185 ASSERT_NE(buf, nullptr);
186 ASSERT_EQ(buf->argc, 8);
187 ASSERT_EQ(buf->stack_argc, 16);
188 ASSERT_EQ(buf->resc, 1);
189 ASSERT_EQ(buf->argv[0], 0U);
190 ASSERT_EQ(buf->argv[1], 0xffff'ffff'ffff'ffffULL);
191 ASSERT_EQ(buf->argv[2], 2U);
192 ASSERT_EQ(buf->argv[3], 3U);
193 ASSERT_EQ(buf->argv[4], 4U);
194 ASSERT_EQ(buf->argv[5], 5U);
195 ASSERT_EQ(buf->argv[6], 6U);
196 ASSERT_EQ(buf->argv[7], 0xffff'ffff'ffff'fff9ULL);
197 ASSERT_EQ(buf->stack_argv[0], 0xffff'ffff'ffff'fff8ULL);
198 ASSERT_EQ(buf->stack_argv[1], 9U);
199 buf->argv[0] = 0xffff'ffff'ffff'fff6;
200 }
201
TEST(CodeGenLib,GenWrapGuestFunction_Run10Int8)202 TEST(CodeGenLib, GenWrapGuestFunction_Run10Int8) {
203 MachineCode machine_code;
204
205 GenWrapGuestFunction(
206 &machine_code, ToGuestAddr(&g_insn), "bbbbbbbbbbb", AsHostCode(Run10Int8), "Run10Int8");
207
208 ScopedExecRegion exec(&machine_code);
209
210 using Func =
211 int8_t(int8_t, int8_t, int8_t, int8_t, int8_t, int8_t, int8_t, int8_t, int8_t, int8_t);
212 int8_t res = exec.get<Func>()(0, -1, 2, 3, 4, 5, 6, -7, -8, 9);
213 ASSERT_EQ(res, -10);
214 }
215
Run10UInt16(GuestAddr pc,GuestArgumentBuffer * buf)216 void Run10UInt16(GuestAddr pc, GuestArgumentBuffer* buf) {
217 ASSERT_EQ(ToGuestAddr(&g_insn), pc);
218 ASSERT_NE(buf, nullptr);
219 ASSERT_EQ(buf->argc, 8);
220 ASSERT_EQ(buf->stack_argc, 16);
221 ASSERT_EQ(buf->resc, 1);
222 ASSERT_EQ(buf->argv[0], 0U);
223 ASSERT_EQ(buf->argv[1], 0xffffU);
224 ASSERT_EQ(buf->argv[2], 2U);
225 ASSERT_EQ(buf->argv[3], 3U);
226 ASSERT_EQ(buf->argv[4], 4U);
227 ASSERT_EQ(buf->argv[5], 5U);
228 ASSERT_EQ(buf->argv[6], 6U);
229 ASSERT_EQ(buf->argv[7], 0xfff9U);
230 ASSERT_EQ(buf->stack_argv[0], 0xfff8U);
231 ASSERT_EQ(buf->stack_argv[1], 9U);
232 buf->argv[0] = 0xfff6;
233 }
234
TEST(CodeGenLib,GenWrapGuestFunction_Run10UInt16)235 TEST(CodeGenLib, GenWrapGuestFunction_Run10UInt16) {
236 MachineCode machine_code;
237
238 GenWrapGuestFunction(
239 &machine_code, ToGuestAddr(&g_insn), "ccccccccccc", AsHostCode(Run10UInt16), "Run10UInt16");
240
241 ScopedExecRegion exec(&machine_code);
242
243 using Func = uint16_t(uint16_t,
244 uint16_t,
245 uint16_t,
246 uint16_t,
247 uint16_t,
248 uint16_t,
249 uint16_t,
250 uint16_t,
251 uint16_t,
252 uint16_t);
253 uint16_t res = exec.get<Func>()(0, 0xffff, 2, 3, 4, 5, 6, 0xfff9, 0xfff8, 9);
254 ASSERT_EQ(res, 0xfff6U);
255 }
256
Run10Int16(GuestAddr pc,GuestArgumentBuffer * buf)257 void Run10Int16(GuestAddr pc, GuestArgumentBuffer* buf) {
258 ASSERT_EQ(ToGuestAddr(&g_insn), pc);
259 ASSERT_NE(buf, nullptr);
260 ASSERT_EQ(buf->argc, 8);
261 ASSERT_EQ(buf->stack_argc, 16);
262 ASSERT_EQ(buf->resc, 1);
263 ASSERT_EQ(buf->argv[0], 0U);
264 ASSERT_EQ(buf->argv[1], 0xffff'ffff'ffff'ffffULL);
265 ASSERT_EQ(buf->argv[2], 2U);
266 ASSERT_EQ(buf->argv[3], 3U);
267 ASSERT_EQ(buf->argv[4], 4U);
268 ASSERT_EQ(buf->argv[5], 5U);
269 ASSERT_EQ(buf->argv[6], 6U);
270 ASSERT_EQ(buf->argv[7], 0xffff'ffff'ffff'fff9ULL);
271 ASSERT_EQ(buf->stack_argv[0], 0xffff'ffff'ffff'fff8ULL);
272 ASSERT_EQ(buf->stack_argv[1], 9U);
273 buf->argv[0] = 0xffff'ffff'ffff'fff6;
274 }
275
TEST(CodeGenLib,GenWrapGuestFunction_Run10Int16)276 TEST(CodeGenLib, GenWrapGuestFunction_Run10Int16) {
277 MachineCode machine_code;
278
279 GenWrapGuestFunction(
280 &machine_code, ToGuestAddr(&g_insn), "sssssssssss", AsHostCode(Run10Int16), "Run10Int16");
281
282 ScopedExecRegion exec(&machine_code);
283
284 using Func = int16_t(
285 int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t);
286 int16_t res = exec.get<Func>()(0, -1, 2, 3, 4, 5, 6, -7, -8, 9);
287 ASSERT_EQ(res, -10);
288 }
289
Run10Int(GuestAddr pc,GuestArgumentBuffer * buf)290 void Run10Int(GuestAddr pc, GuestArgumentBuffer* buf) {
291 ASSERT_EQ(ToGuestAddr(&g_insn), pc);
292 ASSERT_NE(buf, nullptr);
293 ASSERT_EQ(buf->argc, 8);
294 ASSERT_EQ(buf->stack_argc, 16);
295 ASSERT_EQ(buf->resc, 1);
296 ASSERT_EQ(buf->argv[0], 0U);
297 ASSERT_EQ(buf->argv[1], 0xffff'ffff'ffff'ffffULL);
298 ASSERT_EQ(buf->argv[2], 2U);
299 ASSERT_EQ(buf->argv[3], 3U);
300 ASSERT_EQ(buf->argv[4], 4U);
301 ASSERT_EQ(buf->argv[5], 5U);
302 ASSERT_EQ(buf->argv[6], 6U);
303 ASSERT_EQ(buf->argv[7], 0xffff'ffff'ffff'fff9ULL);
304 ASSERT_EQ(buf->stack_argv[0], 0xffff'ffff'ffff'fff8ULL);
305 ASSERT_EQ(buf->stack_argv[1], 9U);
306 buf->argv[0] = 0xffff'ffff'ffff'fff6;
307 }
308
TEST(CodeGenLib,GenWrapGuestFunction_Run10Int)309 TEST(CodeGenLib, GenWrapGuestFunction_Run10Int) {
310 MachineCode machine_code;
311
312 GenWrapGuestFunction(
313 &machine_code, ToGuestAddr(&g_insn), "iiiiiiiiiii", AsHostCode(Run10Int), "Run10Int");
314
315 ScopedExecRegion exec(&machine_code);
316
317 using Func = int(int, int, int, int, int, int, int, int, int, int);
318 int res = exec.get<Func>()(0, -1, 2, 3, 4, 5, 6, -7, -8, 9);
319 ASSERT_EQ(res, -10);
320 }
321
Run18Fp(GuestAddr pc,GuestArgumentBuffer * buf)322 void Run18Fp(GuestAddr pc, GuestArgumentBuffer* buf) {
323 static_assert(sizeof(float) == sizeof(uint32_t));
324 ASSERT_EQ(pc, ToGuestAddr(&g_insn));
325 ASSERT_NE(nullptr, buf);
326 // riscv verification
327 ASSERT_EQ(8, buf->argc);
328 ASSERT_EQ(8, buf->fp_argc);
329 ASSERT_EQ(16, buf->stack_argc);
330 ASSERT_EQ(0, buf->resc);
331 ASSERT_EQ(1, buf->fp_resc);
332 // 32-bit parameters passed in floating-point registers are 1-extended.
333 // 32-bit parameters passed in general-purpose registers and on the stack are 0-extended.
334 ASSERT_EQ(kNanBoxFloat32, buf->fp_argv[0] & kNanBoxFloat32);
335 ASSERT_FLOAT_EQ(0.0f, bit_cast<float>(static_cast<uint32_t>(buf->fp_argv[0])));
336 ASSERT_EQ(kNanBoxFloat32, buf->fp_argv[1] & kNanBoxFloat32);
337 ASSERT_FLOAT_EQ(1.1f, bit_cast<float>(static_cast<uint32_t>(buf->fp_argv[1])));
338 ASSERT_EQ(kNanBoxFloat32, buf->fp_argv[2] & kNanBoxFloat32);
339 ASSERT_FLOAT_EQ(2.2f, bit_cast<float>(static_cast<uint32_t>(buf->fp_argv[2])));
340 ASSERT_EQ(kNanBoxFloat32, buf->fp_argv[3] & kNanBoxFloat32);
341 ASSERT_FLOAT_EQ(3.3f, bit_cast<float>(static_cast<uint32_t>(buf->fp_argv[3])));
342 ASSERT_EQ(kNanBoxFloat32, buf->fp_argv[4] & kNanBoxFloat32);
343 ASSERT_FLOAT_EQ(4.4f, bit_cast<float>(static_cast<uint32_t>(buf->fp_argv[4])));
344 ASSERT_EQ(kNanBoxFloat32, buf->fp_argv[5] & kNanBoxFloat32);
345 ASSERT_FLOAT_EQ(5.5f, bit_cast<float>(static_cast<uint32_t>(buf->fp_argv[5])));
346 ASSERT_EQ(kNanBoxFloat32, buf->fp_argv[6] & kNanBoxFloat32);
347 ASSERT_FLOAT_EQ(6.6f, bit_cast<float>(static_cast<uint32_t>(buf->fp_argv[6])));
348 ASSERT_EQ(kNanBoxFloat32, buf->fp_argv[7] & kNanBoxFloat32);
349 ASSERT_FLOAT_EQ(7.7f, bit_cast<float>(static_cast<uint32_t>(buf->fp_argv[7])));
350 ASSERT_FLOAT_EQ(8.8f, bit_cast<float>(static_cast<uint32_t>(buf->argv[0])));
351 ASSERT_FLOAT_EQ(9.9f, bit_cast<float>(static_cast<uint32_t>(buf->argv[1])));
352 ASSERT_FLOAT_EQ(10.01f, bit_cast<float>(static_cast<uint32_t>(buf->argv[2])));
353 ASSERT_FLOAT_EQ(20.02f, bit_cast<float>(static_cast<uint32_t>(buf->argv[3])));
354 ASSERT_FLOAT_EQ(30.03f, bit_cast<float>(static_cast<uint32_t>(buf->argv[4])));
355 ASSERT_FLOAT_EQ(40.04f, bit_cast<float>(static_cast<uint32_t>(buf->argv[5])));
356 ASSERT_FLOAT_EQ(50.05f, bit_cast<float>(static_cast<uint32_t>(buf->argv[6])));
357 ASSERT_FLOAT_EQ(60.06f, bit_cast<float>(static_cast<uint32_t>(buf->argv[7])));
358 ASSERT_FLOAT_EQ(70.07f, bit_cast<float>(static_cast<uint32_t>(buf->stack_argv[0])));
359 ASSERT_FLOAT_EQ(80.08f, bit_cast<float>(static_cast<uint32_t>(buf->stack_argv[1])));
360 buf->fp_argv[0] = static_cast<uint64_t>(bit_cast<uint32_t>(45.45f)) | kNanBoxFloat32;
361 }
362
TEST(CodeGenLib,GenWrapGuestFunction_Run10Fp)363 TEST(CodeGenLib, GenWrapGuestFunction_Run10Fp) {
364 MachineCode machine_code;
365
366 GenWrapGuestFunction(
367 &machine_code, ToGuestAddr(&g_insn), "fffffffffffffffffff", AsHostCode(Run18Fp), "Run18Fp");
368
369 ScopedExecRegion exec(&machine_code);
370
371 using Func = float(float,
372 float,
373 float,
374 float,
375 float,
376 float,
377 float,
378 float,
379 float,
380 float,
381 float,
382 float,
383 float,
384 float,
385 float,
386 float,
387 float,
388 float);
389 float res = exec.get<Func>()(0.0f,
390 1.1f,
391 2.2f,
392 3.3f,
393 4.4f,
394 5.5f,
395 6.6f,
396 7.7f,
397 8.8f,
398 9.9f,
399 10.01f,
400 20.02f,
401 30.03f,
402 40.04f,
403 50.05f,
404 60.06f,
405 70.07f,
406 80.08f);
407 ASSERT_FLOAT_EQ(45.45f, res);
408 }
409
410 } // namespace
411
412 } // namespace berberis
413