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 <chrono>  // chrono_literals::operator""ms
20 #include <string>
21 #include <thread>  // this_thread::sleep_for
22 
23 #include "berberis/guest_state/guest_addr.h"
24 #include "berberis/runtime_primitives/host_code.h"
25 #include "berberis/runtime_primitives/runtime_library.h"  // kEntry*
26 #include "berberis/runtime_primitives/translation_cache.h"
27 
28 namespace berberis {
29 
30 namespace {
31 
32 using std::chrono_literals::operator""ms;
33 
TEST(TranslationCacheTest,DefaultNotTranslated)34 TEST(TranslationCacheTest, DefaultNotTranslated) {
35   constexpr GuestAddr pc = 0x12345678;
36 
37   TranslationCache tc;
38 
39   EXPECT_EQ(tc.GetHostCodePtr(pc)->load(), kEntryNotTranslated);
40   EXPECT_EQ(tc.GetHostCodePtr(pc + 1024)->load(), kEntryNotTranslated);
41   EXPECT_EQ(tc.GetInvocationCounter(pc), 0U);
42 }
43 
TEST(TranslationCacheTest,UpdateInvocationCounter)44 TEST(TranslationCacheTest, UpdateInvocationCounter) {
45   constexpr GuestAddr pc = 0x12345678;
46 
47   TranslationCache tc;
48 
49   // Create entry
50   GuestCodeEntry* entry = tc.AddAndLockForTranslation(pc, 0);
51   ASSERT_TRUE(entry);
52   EXPECT_EQ(entry->invocation_counter, 0U);
53   entry->invocation_counter = 42;
54   tc.SetTranslatedAndUnlock(pc, entry, 1, GuestCodeEntry::Kind::kSpecialHandler, {kEntryNoExec, 0});
55 
56   EXPECT_EQ(tc.GetInvocationCounter(pc), 42U);
57 }
58 
TEST(TranslationCacheTest,AddAndLockForTranslation)59 TEST(TranslationCacheTest, AddAndLockForTranslation) {
60   constexpr GuestAddr pc = 0x12345678;
61 
62   TranslationCache tc;
63 
64   // Cannot lock if counter is below the threshold, but entry is created anyway.
65   ASSERT_FALSE(tc.AddAndLockForTranslation(pc, 1));
66   GuestCodeEntry* entry = tc.LookupGuestCodeEntryUnsafeForTesting(pc);
67   ASSERT_TRUE(entry);
68   EXPECT_EQ(tc.GetHostCodePtr(pc)->load(), kEntryNotTranslated);
69   EXPECT_EQ(entry->kind, GuestCodeEntry::Kind::kInterpreted);
70   EXPECT_EQ(tc.GetInvocationCounter(pc), 1U);
71 
72   // Lock when counter is equal or above the threshold.
73   entry = tc.AddAndLockForTranslation(pc, 1);
74   ASSERT_TRUE(entry);
75   EXPECT_EQ(tc.GetHostCodePtr(pc)->load(), kEntryTranslating);
76   EXPECT_EQ(entry->kind, GuestCodeEntry::Kind::kUnderProcessing);
77   EXPECT_EQ(tc.GetInvocationCounter(pc), 1U);
78 
79   // Cannot lock locked.
80   ASSERT_FALSE(tc.AddAndLockForTranslation(pc, 0));
81 
82   // Unlock.
83   tc.SetTranslatedAndUnlock(pc, entry, 1, GuestCodeEntry::Kind::kSpecialHandler, {kEntryNoExec, 0});
84   EXPECT_EQ(tc.GetHostCodePtr(pc)->load(), kEntryNoExec);
85   EXPECT_EQ(entry->kind, GuestCodeEntry::Kind::kSpecialHandler);
86 
87   // Cannot lock translated.
88   ASSERT_FALSE(tc.AddAndLockForTranslation(pc, 0));
89 }
90 
91 constexpr bool kWrappedHostFunc = true;
92 
TEST(TranslationCacheTest,AddAndLockForWrapping)93 TEST(TranslationCacheTest, AddAndLockForWrapping) {
94   constexpr GuestAddr pc = 0x12345678;
95 
96   TranslationCache tc;
97 
98   // Add and lock nonexistent.
99   GuestCodeEntry* entry = tc.AddAndLockForWrapping(pc);
100   ASSERT_TRUE(entry);
101   ASSERT_EQ(kEntryWrapping, tc.GetHostCodePtr(pc)->load());
102   ASSERT_EQ(entry->kind, GuestCodeEntry::Kind::kUnderProcessing);
103 
104   // Cannot lock locked.
105   ASSERT_FALSE(tc.AddAndLockForWrapping(pc));
106 
107   // Unlock.
108   tc.SetWrappedAndUnlock(pc, entry, kWrappedHostFunc, {kEntryNoExec, 0});
109   ASSERT_EQ(kEntryNoExec, tc.GetHostCodePtr(pc)->load());
110   ASSERT_EQ(entry->kind, GuestCodeEntry::Kind::kHostWrapped);
111 
112   // Cannot lock wrapped.
113   ASSERT_FALSE(tc.AddAndLockForWrapping(pc));
114 
115   // Cannot lock not translated but already interpreted.
116   ASSERT_FALSE(tc.AddAndLockForTranslation(pc + 64, 1));
117   entry = tc.LookupGuestCodeEntryUnsafeForTesting(pc + 64);
118   ASSERT_TRUE(entry);
119   ASSERT_EQ(entry->kind, GuestCodeEntry::Kind::kInterpreted);
120   ASSERT_FALSE(tc.AddAndLockForWrapping(pc + 64));
121 }
122 
123 HostCodeAddr kHostCodeStub = AsHostCodeAddr(AsHostCode(0xdeadbeef));
124 
TestWrappingWorker(TranslationCache * tc,GuestAddr pc)125 void TestWrappingWorker(TranslationCache* tc, GuestAddr pc) {
126   while (true) {
127     GuestCodeEntry* entry = tc->AddAndLockForWrapping(pc);
128     if (entry) {
129       EXPECT_EQ(entry->host_code->load(), kEntryWrapping);
130       EXPECT_EQ(entry->kind, GuestCodeEntry::Kind::kUnderProcessing);
131       // Give other threads some time to run the loop. Typical test
132       // time is 1 ms, make sleep order of magnitude longer - 10ms.
133       std::this_thread::sleep_for(10ms);
134       tc->SetWrappedAndUnlock(pc, entry, kWrappedHostFunc, {kHostCodeStub, 0});
135       EXPECT_EQ(entry->host_code->load(), kHostCodeStub);
136       EXPECT_EQ(entry->kind, GuestCodeEntry::Kind::kHostWrapped);
137       return;
138     }
139 
140     auto host_code = tc->GetHostCodePtr(pc)->load();
141 
142     // Warning: the order of comparisons here is
143     // important since code can change in between.
144     if (host_code == kEntryWrapping) {
145       continue;
146     }
147 
148     EXPECT_EQ(host_code, kHostCodeStub);
149     break;
150   }
151 
152   return;
153 }
154 
TestTranslationWorker(TranslationCache * tc,GuestAddr pc)155 void TestTranslationWorker(TranslationCache* tc, GuestAddr pc) {
156   while (true) {
157     GuestCodeEntry* entry = tc->AddAndLockForTranslation(pc, 0);
158     if (entry) {
159       EXPECT_EQ(entry->host_code->load(), kEntryTranslating);
160       EXPECT_EQ(entry->kind, GuestCodeEntry::Kind::kUnderProcessing);
161       // Give other threads some time to run the loop. Typical test
162       // time is 1 ms, make sleep order of magnitude longer - 10ms.
163       std::this_thread::sleep_for(10ms);
164       tc->SetTranslatedAndUnlock(
165           pc, entry, 1, GuestCodeEntry::Kind::kSpecialHandler, {kHostCodeStub, 0});
166       EXPECT_EQ(entry->host_code->load(), kHostCodeStub);
167       return;
168     }
169 
170     auto host_code = tc->GetHostCodePtr(pc)->load();
171     if (host_code == kEntryTranslating) {
172       continue;
173     }
174     EXPECT_EQ(host_code, kHostCodeStub);
175     break;
176   }
177 
178   return;
179 }
180 
181 template <void(WorkerFunc)(TranslationCache*, GuestAddr)>
TranslationCacheTestRunThreads()182 void TranslationCacheTestRunThreads() {
183   TranslationCache tc;
184   constexpr uint32_t kNumThreads = 16;
185   std::array<std::thread, kNumThreads> threads;
186 
187   // First test situation, when every thread has its own pc.
188   for (uint32_t i = 0; i < kNumThreads; i++) {
189     threads[i] = std::thread(WorkerFunc, &tc, i);
190   }
191 
192   for (auto& thread : threads) {
193     thread.join();
194   }
195 
196   // Now introduce heavy contention.
197   GuestAddr pc = 0x12345678;
198   for (auto& thread : threads) {
199     thread = std::thread(WorkerFunc, &tc, pc);
200   }
201 
202   for (auto& thread : threads) {
203     thread.join();
204   }
205 }
206 
TEST(TranslationCacheTest,InvalidateNotTranslated)207 TEST(TranslationCacheTest, InvalidateNotTranslated) {
208   constexpr GuestAddr pc = 0x12345678;
209 
210   TranslationCache tc;
211 
212   ASSERT_EQ(kEntryNotTranslated, tc.GetHostCodePtr(pc)->load());
213 
214   tc.InvalidateGuestRange(pc, pc + 1);
215 
216   // Not translated stays not translated
217   ASSERT_EQ(kEntryNotTranslated, tc.GetHostCodePtr(pc)->load());
218   ASSERT_FALSE(tc.LookupGuestCodeEntryUnsafeForTesting(pc));
219 }
220 
TEST(TranslationCacheTest,InvalidateTranslated)221 TEST(TranslationCacheTest, InvalidateTranslated) {
222   constexpr GuestAddr pc = 0x12345678;
223   const auto host_code = AsHostCodeAddr(AsHostCode(0xdeadbeef));
224 
225   TranslationCache tc;
226 
227   GuestCodeEntry* entry = tc.AddAndLockForTranslation(pc, 0);
228   ASSERT_TRUE(entry);
229   ASSERT_EQ(kEntryTranslating, tc.GetHostCodePtr(pc)->load());
230 
231   tc.SetTranslatedAndUnlock(pc, entry, 1, GuestCodeEntry::Kind::kHeavyOptimized, {host_code, 4});
232   ASSERT_EQ(host_code, tc.GetHostCodePtr(pc)->load());
233 
234   tc.InvalidateGuestRange(pc, pc + 1);
235 
236   ASSERT_EQ(kEntryNotTranslated, tc.GetHostCodePtr(pc)->load());
237   ASSERT_FALSE(tc.LookupGuestCodeEntryUnsafeForTesting(pc));
238 }
239 
TEST(TranslationCacheTest,InvalidateTranslating)240 TEST(TranslationCacheTest, InvalidateTranslating) {
241   constexpr GuestAddr pc = 0x12345678;
242   const auto host_code = AsHostCodeAddr(AsHostCode(0xdeadbeef));
243 
244   TranslationCache tc;
245 
246   GuestCodeEntry* entry = tc.AddAndLockForTranslation(pc, 0);
247   ASSERT_TRUE(entry);
248   ASSERT_EQ(kEntryTranslating, tc.GetHostCodePtr(pc)->load());
249 
250   tc.InvalidateGuestRange(pc, pc + 1);
251   ASSERT_EQ(kEntryInvalidating, tc.GetHostCodePtr(pc)->load());
252   entry = tc.LookupGuestCodeEntryUnsafeForTesting(pc);
253   ASSERT_TRUE(entry);
254   ASSERT_EQ(entry->kind, GuestCodeEntry::Kind::kUnderProcessing);
255 
256   tc.SetTranslatedAndUnlock(pc, entry, 1, GuestCodeEntry::Kind::kSpecialHandler, {host_code, 4});
257   ASSERT_EQ(kEntryNotTranslated, tc.GetHostCodePtr(pc)->load());
258   ASSERT_FALSE(tc.LookupGuestCodeEntryUnsafeForTesting(pc));
259 }
260 
TEST(TranslationCacheTest,InvalidateTranslatingOutOfRange)261 TEST(TranslationCacheTest, InvalidateTranslatingOutOfRange) {
262   constexpr GuestAddr pc = 0x12345678;
263   const auto host_code = AsHostCodeAddr(AsHostCode(0xdeadbeef));
264 
265   TranslationCache tc;
266 
267   GuestCodeEntry* entry = tc.AddAndLockForTranslation(pc, 0);
268   ASSERT_TRUE(entry);
269   ASSERT_EQ(kEntryTranslating, tc.GetHostCodePtr(pc)->load());
270 
271   // Invalidate range that does *not* contain translating address.
272   // The entry should still be invalidated, as translated region is only known after translation,
273   // and it might overlap with the invalidated range.
274   tc.InvalidateGuestRange(pc + 100, pc + 101);
275   ASSERT_EQ(kEntryInvalidating, tc.GetHostCodePtr(pc)->load());
276 
277   tc.SetTranslatedAndUnlock(pc, entry, 1, GuestCodeEntry::Kind::kSpecialHandler, {host_code, 4});
278   ASSERT_EQ(kEntryNotTranslated, tc.GetHostCodePtr(pc)->load());
279 }
280 
Translate(TranslationCache * tc,GuestAddr pc,uint32_t size,HostCodeAddr host_code)281 bool Translate(TranslationCache* tc, GuestAddr pc, uint32_t size, HostCodeAddr host_code) {
282   GuestCodeEntry* entry = tc->AddAndLockForTranslation(pc, 0);
283   if (!entry) {
284     return false;
285   }
286   tc->SetTranslatedAndUnlock(
287       pc, entry, size, GuestCodeEntry::Kind::kSpecialHandler, {host_code, 4});
288   return true;
289 }
290 
TEST(TranslationCacheTest,LockForGearUpTranslation)291 TEST(TranslationCacheTest, LockForGearUpTranslation) {
292   constexpr GuestAddr pc = 0x12345678;
293   const auto host_code = AsHostCodeAddr(AsHostCode(0xdeadbeef));
294 
295   TranslationCache tc;
296 
297   // Cannot lock if not yet added.
298   ASSERT_FALSE(tc.LockForGearUpTranslation(pc));
299 
300   ASSERT_TRUE(Translate(&tc, pc + 0, 1, host_code));
301   GuestCodeEntry* entry = tc.LookupGuestCodeEntryUnsafeForTesting(pc);
302   ASSERT_TRUE(entry);
303   ASSERT_EQ(entry->kind, GuestCodeEntry::Kind::kSpecialHandler);
304 
305   // Cannot lock if kind is not kLiteTranslated.
306   ASSERT_FALSE(tc.LockForGearUpTranslation(pc));
307 
308   entry->kind = GuestCodeEntry::Kind::kLiteTranslated;
309 
310   entry = tc.LockForGearUpTranslation(pc);
311   ASSERT_TRUE(entry);
312   ASSERT_EQ(kEntryTranslating, tc.GetHostCodePtr(pc)->load());
313   ASSERT_EQ(entry->kind, GuestCodeEntry::Kind::kUnderProcessing);
314 
315   // Unlock.
316   tc.SetTranslatedAndUnlock(pc, entry, 1, GuestCodeEntry::Kind::kHeavyOptimized, {kEntryNoExec, 0});
317   ASSERT_EQ(kEntryNoExec, tc.GetHostCodePtr(pc)->load());
318   ASSERT_EQ(entry->kind, GuestCodeEntry::Kind::kHeavyOptimized);
319 
320   // Cannot lock translated.
321   ASSERT_FALSE(tc.AddAndLockForTranslation(pc, 0));
322 }
323 
TEST(TranslationCacheTest,InvalidateRange)324 TEST(TranslationCacheTest, InvalidateRange) {
325   constexpr GuestAddr pc = 0x12345678;
326   const auto host_code = AsHostCodeAddr(AsHostCode(0xdeadbeef));
327 
328   TranslationCache tc;
329 
330   ASSERT_TRUE(Translate(&tc, pc + 0, 1, host_code));
331   ASSERT_TRUE(Translate(&tc, pc + 1, 1, host_code));
332   ASSERT_TRUE(Translate(&tc, pc + 2, 1, host_code));
333 
334   ASSERT_EQ(host_code, tc.GetHostCodePtr(pc + 0)->load());
335   ASSERT_EQ(host_code, tc.GetHostCodePtr(pc + 1)->load());
336   ASSERT_EQ(host_code, tc.GetHostCodePtr(pc + 2)->load());
337 
338   tc.InvalidateGuestRange(pc + 1, pc + 2);
339 
340   ASSERT_EQ(host_code, tc.GetHostCodePtr(pc + 0)->load());
341   ASSERT_EQ(kEntryNotTranslated, tc.GetHostCodePtr(pc + 1)->load());
342   ASSERT_EQ(host_code, tc.GetHostCodePtr(pc + 2)->load());
343 }
344 
Wrap(TranslationCache * tc,GuestAddr pc,HostCodeAddr host_code)345 bool Wrap(TranslationCache* tc, GuestAddr pc, HostCodeAddr host_code) {
346   GuestCodeEntry* entry = tc->AddAndLockForWrapping(pc);
347   if (!entry) {
348     return false;
349   }
350   tc->SetWrappedAndUnlock(pc, entry, kWrappedHostFunc, {host_code, 0});
351   return true;
352 }
353 
TEST(TranslationCacheTest,InvalidateWrapped)354 TEST(TranslationCacheTest, InvalidateWrapped) {
355   constexpr GuestAddr pc = 0x12345678;
356 
357   TranslationCache tc;
358 
359   ASSERT_TRUE(Wrap(&tc, pc, kEntryNoExec));
360 
361   tc.InvalidateGuestRange(pc, pc + 1);
362 
363   ASSERT_EQ(kEntryNotTranslated, tc.GetHostCodePtr(pc)->load());
364 }
365 
TEST(TranslationCacheTest,InvalidateWrappingWrap)366 TEST(TranslationCacheTest, InvalidateWrappingWrap) {
367   constexpr GuestAddr pc = 0x12345678;
368 
369   TranslationCache tc;
370 
371   GuestCodeEntry* entry = tc.AddAndLockForWrapping(pc);
372   ASSERT_TRUE(entry);
373 
374   tc.InvalidateGuestRange(pc, pc + 1);
375   ASSERT_EQ(kEntryInvalidating, tc.GetHostCodePtr(pc)->load());
376 
377   tc.SetWrappedAndUnlock(pc, entry, kWrappedHostFunc, {kEntryNoExec, 0});
378   ASSERT_EQ(kEntryNotTranslated, tc.GetHostCodePtr(pc)->load());
379 
380   ASSERT_TRUE(Wrap(&tc, pc, kEntryNoExec));
381 }
382 
TEST(TranslationCacheTest,WrapInvalidateWrap)383 TEST(TranslationCacheTest, WrapInvalidateWrap) {
384   constexpr GuestAddr pc = 0x12345678;
385 
386   TranslationCache tc;
387 
388   ASSERT_TRUE(Wrap(&tc, pc, kEntryNoExec));
389 
390   tc.InvalidateGuestRange(pc, pc + 1);
391 
392   ASSERT_TRUE(Wrap(&tc, pc, kEntryNoExec));
393 }
394 
TEST(TranslationCacheTest,WrapInvalidateTranslate)395 TEST(TranslationCacheTest, WrapInvalidateTranslate) {
396   constexpr GuestAddr pc = 0x12345678;
397 
398   TranslationCache tc;
399 
400   ASSERT_TRUE(Wrap(&tc, pc, kEntryNoExec));
401 
402   tc.InvalidateGuestRange(pc, pc + 1);
403 
404   ASSERT_TRUE(Translate(&tc, pc, 1, kEntryNoExec));
405 }
406 
TEST(NdkTest,TranslationCacheWrappingStatesTest)407 TEST(NdkTest, TranslationCacheWrappingStatesTest) {
408   TranslationCacheTestRunThreads<TestWrappingWorker>();
409 }
410 
TEST(NdkTest,TranslationCacheTranslationStatesTest)411 TEST(NdkTest, TranslationCacheTranslationStatesTest) {
412   TranslationCacheTestRunThreads<TestTranslationWorker>();
413 }
414 
415 }  // namespace
416 
417 }  // namespace berberis
418