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 <setjmp.h>
20 #include <cstdint>
21
22 #include "berberis/assembler/machine_code.h"
23 #include "berberis/guest_state/guest_addr.h"
24 #include "berberis/guest_state/guest_state.h"
25 #include "berberis/lite_translator/lite_translate_region.h"
26 #include "berberis/test_utils/scoped_exec_region.h"
27 #include "berberis/test_utils/testing_run_generated_code.h"
28
29 namespace berberis {
30
31 namespace {
32
33 class Riscv64LiteTranslateRegionTest : public ::testing::Test {
34 public:
35 template <typename T>
Reset(const T code)36 void Reset(const T code) {
37 state_.cpu.insn_addr = ToGuestAddr(code);
38 }
39
40 // Attention: it's important to pass code array by reference for sizeof(code) to return the size
41 // of the whole array rather than a pointer size when it's passed by value.
42 template <typename T>
Run(T & code,GuestAddr expected_stop_addr)43 bool Run(T& code, GuestAddr expected_stop_addr) {
44 Reset(code);
45 GuestAddr code_end = ToGuestAddr(bit_cast<char*>(&code[0]) + sizeof(code));
46 MachineCode machine_code;
47 bool success = LiteTranslateRange(state_.cpu.insn_addr,
48 code_end,
49 &machine_code,
50 LiteTranslateParams{.allow_dispatch = false});
51
52 if (!success) {
53 return false;
54 }
55
56 ScopedExecRegion exec(&machine_code);
57
58 TestingRunGeneratedCode(&state_, exec.get(), expected_stop_addr);
59
60 // Make sure we print the addresses on mismatch.
61 EXPECT_EQ(state_.cpu.insn_addr, expected_stop_addr);
62 return true;
63 }
64
65 protected:
66 ThreadState state_;
67 };
68
TEST_F(Riscv64LiteTranslateRegionTest,AddTwice)69 TEST_F(Riscv64LiteTranslateRegionTest, AddTwice) {
70 static const uint32_t code[] = {
71 0x003100b3, // add x1, x2, x3
72 0x002081b3, // add x3, x1, x2
73 };
74 SetXReg<1>(state_.cpu, 0);
75 SetXReg<2>(state_.cpu, 1);
76 SetXReg<3>(state_.cpu, 1);
77 EXPECT_TRUE(Run(code, ToGuestAddr(bit_cast<char*>(&code[0]) + sizeof(code))));
78 EXPECT_EQ(GetXReg<3>(state_.cpu), 3ULL);
79 }
80
TEST_F(Riscv64LiteTranslateRegionTest,XorLoop)81 TEST_F(Riscv64LiteTranslateRegionTest, XorLoop) {
82 static const uint16_t code[] = {
83 // loop_enter:
84 0x161b, // (4 bytes sllw instruction)
85 0x0015, // sllw a2,a0,0x1
86 0x35fd, // addw a1,a1,-1
87 0x8d31, // xor a0,a0,a2
88 0xfde5, // bnez a1, loop_enter
89 };
90 SetXReg<A0>(state_.cpu, 1);
91 // The counter will be equal one after decrement, so we expected to branch back.
92 SetXReg<A1>(state_.cpu, 2);
93 SetXReg<A2>(state_.cpu, 0);
94 EXPECT_TRUE(Run(code, ToGuestAddr(&code[0])));
95 EXPECT_EQ(GetXReg<A0>(state_.cpu), uint64_t{0b11});
96 }
97
TEST_F(Riscv64LiteTranslateRegionTest,RegionEnd)98 TEST_F(Riscv64LiteTranslateRegionTest, RegionEnd) {
99 static const uint32_t code[] = {
100 0x003100b3, // add x1, x2, x3
101 0x002081b3, // add x3, x1, x2
102 0x008000ef, // jal x1, 8
103 0x003100b3, // add x1, x2, x3
104 0x002081b3, // add x3, x1, x2
105 };
106 SetXReg<1>(state_.cpu, 0);
107 SetXReg<2>(state_.cpu, 1);
108 SetXReg<3>(state_.cpu, 1);
109 EXPECT_TRUE(Run(code, ToGuestAddr(code) + 16));
110 EXPECT_EQ(GetXReg<3>(state_.cpu), 3ULL);
111 }
112
TEST_F(Riscv64LiteTranslateRegionTest,GracefulFailure)113 TEST_F(Riscv64LiteTranslateRegionTest, GracefulFailure) {
114 static const uint32_t code[] = {
115 0x003100b3, // add x1, x2, x3
116 0x00000073, // ecall #0x0
117 };
118 MachineCode machine_code;
119 EXPECT_FALSE(LiteTranslateRange(ToGuestAddr(code),
120 ToGuestAddr(code) + 8,
121 &machine_code,
122 LiteTranslateParams{.allow_dispatch = false}));
123 }
124
125 jmp_buf g_jmp_buf;
126
127 extern "C" __attribute__((used, __visibility__("hidden"))) void
LightTranslateRegionTest_HandleThresholdReached()128 LightTranslateRegionTest_HandleThresholdReached() {
129 // We are in generated code, so the easiest way to recover without using
130 // runtime library internals is to longjmp.
131 longjmp(g_jmp_buf, 1);
132 }
133
134 // The execution jumps here (no call!) from generated code. Thus we
135 // need this proxy to normal C++ ABI function. Stack in generated code is
136 // aligned properly for calls.
CounterThresholdReached()137 __attribute__((naked)) void CounterThresholdReached() {
138 asm(R"(call LightTranslateRegionTest_HandleThresholdReached)");
139 }
140
TEST_F(Riscv64LiteTranslateRegionTest,ProfileCounter)141 TEST_F(Riscv64LiteTranslateRegionTest, ProfileCounter) {
142 static const uint16_t code[] = {
143 0x0505, // addi a0,a0,1
144 };
145
146 // Volatile to ensure it's correctly restored on longjmp.
147 volatile ThreadState state;
148 GuestAddr code_end = ToGuestAddr(code + 1);
149
150 MachineCode machine_code;
151 uint32_t counter;
152 constexpr uint32_t kCounterThreshold = 42;
153 bool success = LiteTranslateRange(
154 ToGuestAddr(code),
155 code_end,
156 &machine_code,
157 {
158 .enable_self_profiling = true,
159 .counter_location = &counter,
160 .counter_threshold = kCounterThreshold,
161 .counter_threshold_callback = reinterpret_cast<const void*>(CounterThresholdReached),
162 });
163 ASSERT_TRUE(success);
164
165 ScopedExecRegion exec(&machine_code);
166
167 if (setjmp(g_jmp_buf) != 0) {
168 // We should trap here after longjmp.
169 EXPECT_EQ(state.cpu.x[10], kCounterThreshold);
170 return;
171 }
172
173 state.cpu.x[10] = 0;
174 // We shouldn't ever reach above kCounterThreshold, but keeping this exit condition
175 // to handle a potential failure gracefully.
176 for (uint64_t i = 0; i <= kCounterThreshold; i++) {
177 state.cpu.insn_addr = ToGuestAddr(code);
178 // The API accepts non-volatile state.
179 TestingRunGeneratedCode(const_cast<ThreadState*>(&state), exec.get(), code_end);
180 EXPECT_EQ(state.cpu.insn_addr, code_end);
181 EXPECT_EQ(state.cpu.x[10], i + 1);
182 }
183 FAIL();
184 }
185
186 } // namespace
187
188 } // namespace berberis
189