• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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