1 /*
2  * Copyright (C) 2024 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 "berberis/runtime_primitives/runtime_library.h"
18 
19 #include "berberis/base/config.h"
20 #include "berberis/guest_state/guest_state.h"
21 
22 extern "C" void berberis_HandleNotTranslated(berberis::ThreadState* state);
23 extern "C" void berberis_GetDispatchAddress(berberis::ThreadState* state);
24 extern "C" void berberis_HandleInterpret(berberis::ThreadState* state);
25 
26 // Helpers ensure that the functions below are available in PLT.
helper_NotTranslated(berberis::ThreadState * state)27 __attribute__((used, __visibility__("hidden"))) extern "C" void helper_NotTranslated(
28     berberis::ThreadState* state) {
29   berberis_HandleNotTranslated(state);
30 }
31 
helper_GetDispatchAddress(berberis::ThreadState * state)32 __attribute__((used, __visibility__("hidden"))) extern "C" void helper_GetDispatchAddress(
33     berberis::ThreadState* state) {
34   berberis_GetDispatchAddress(state);
35 }
36 
helper_HandleInterpret(berberis::ThreadState * state)37 __attribute__((used, __visibility__("hidden"))) extern "C" void helper_HandleInterpret(
38     berberis::ThreadState* state) {
39   berberis_HandleInterpret(state);
40 }
41 
42 // Perform all the steps needed to exit generated code except return, which is
43 // up to the users of this macro. The users of this macro may choose to perform
44 // a sibling call as necessary.
45 // clang-format off
46 #define END_GENERATED_CODE(EXIT_INSN)                                   \
47   asm(                                                                  \
48       /* Sync insn_addr. */                                             \
49       "sd s11, %[InsnAddr](fp)\n"                                       \
50       /* Set kOutsideGeneratedCode residence. */                        \
51       "li t1, %[OutsideGeneratedCode]\n"                                \
52       "sb t1, %[Residence](fp)\n"                                       \
53                                                                         \
54       /* Set a0 to the pointer to the guest state so that               \
55        * we can perform a sibling call to functions like                \
56        * berberis_HandleNotTranslated.                                  \
57        */                                                               \
58       "mv a0, fp\n"                                                     \
59                                                                         \
60       /* Epilogue */                                                    \
61       "ld ra, 96(sp)\n"                                                 \
62       "ld fp, 88(sp)\n"                                                 \
63       "ld s1, 80(sp)\n"                                                 \
64       "ld s2, 72(sp)\n"                                                 \
65       "ld s3, 64(sp)\n"                                                 \
66       "ld s4, 56(sp)\n"                                                 \
67       "ld s5, 48(sp)\n"                                                 \
68       "ld s6, 40(sp)\n"                                                 \
69       "ld s7, 32(sp)\n"                                                 \
70       "ld s8, 24(sp)\n"                                                 \
71       "ld s9, 16(sp)\n"                                                 \
72       "ld s10, 8(sp)\n"                                                 \
73       "ld s11, 0(sp)\n"                                                 \
74       "addi sp, sp, 112\n"                                              \
75                                                                         \
76       EXIT_INSN                                                         \
77       ::[InsnAddr] "I"(offsetof(berberis::ThreadState, cpu.insn_addr)), \
78       [Residence] "I"(offsetof(berberis::ThreadState, residence)),      \
79       [OutsideGeneratedCode] "I"(berberis::kOutsideGeneratedCode))
80 // clang-format on
81 
82 namespace berberis {
83 
84 extern "C" {
85 
berberis_RunGeneratedCode(ThreadState * state,HostCode code)86 [[gnu::naked]] [[gnu::noinline]] void berberis_RunGeneratedCode(ThreadState* state, HostCode code) {
87   // Parameters are in a0 - state and a1 - code.
88   // Instruction address is saved in s11. This is also the last register to be allocated within a
89   // region. This approach maximizes the chance of s11 being not clobbered and thus facilitates
90   // debugging.
91   //
92   // On riscv64 Linux, stack should be aligned on 16 at every call insn.
93   // That means stack is always 0 mod 16 on function entry.
94   // See https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf (18.2).
95   //
96   // We are saving all general purpose callee saved registers.
97   // TODO(b/352784623): Save fp registers when we start using them.
98 
99   // clang-format off
100   asm(
101     // Prologue
102       "addi sp, sp, -112\n"
103       "sd s11, 0(sp)\n"
104       "sd s10, 8(sp)\n"
105       "sd s9, 16(sp)\n"
106       "sd s8, 24(sp)\n"
107       "sd s7, 32(sp)\n"
108       "sd s6, 40(sp)\n"
109       "sd s5, 48(sp)\n"
110       "sd s4, 56(sp)\n"
111       "sd s3, 64(sp)\n"
112       "sd s2, 72(sp)\n"
113       "sd s1, 80(sp)\n"
114       "sd fp, 88(sp)\n"
115       "sd ra, 96(sp)\n"
116 
117       // Set state pointer.
118       "mv fp, a0\n"  // kStateRegister, kOmitFramePointer
119 
120       // Set insn_addr.
121       "ld s11, %[InsnAddr](fp)\n"
122       // Set kInsideGeneratedCode residence.
123       "li t1, %[InsideGeneratedCode]\n"
124       "sb t1, %[Residence](fp)\n"
125 
126       // Jump to entry.
127       "jr a1\n"
128       ::[InsnAddr] "I"(offsetof(ThreadState, cpu.insn_addr)),
129   [Residence] "I"(offsetof(ThreadState, residence)),
130   [InsideGeneratedCode] "I"(kInsideGeneratedCode));
131   // clang-format on
132 }
133 
berberis_entry_Interpret()134 [[gnu::naked]] [[gnu::noinline]] void berberis_entry_Interpret() {
135   // clang-format off
136   asm(
137     //Sync insn_addr.
138       "sd s11, %[InsnAddr](fp)\n"
139       // Set kOutsideGeneratedCode residence.
140       "li t0, %[OutsideGeneratedCode]\n"
141       "sb t0, %[Residence](fp)\n"
142 
143       // fp holds the pointer to state which is the argument to the call.
144       "mv a0, fp\n"
145       "call berberis_HandleInterpret@plt\n"
146 
147       // a0 may be clobbered by the call abobe, so init it again.
148       "mv a0, fp\n"
149       "call berberis_GetDispatchAddress@plt\n"
150       "mv t1, a0\n"
151 
152       // Set insn_addr.
153       "ld s11, %[InsnAddr](fp)\n"
154       // Set kInsideGeneratedCode residence.
155       "li t0, %[InsideGeneratedCode]\n"
156       "sb t0, %[Residence](fp)\n"
157 
158       "jr t1\n"
159       ::[InsnAddr] "I"(offsetof(berberis::ThreadState, cpu.insn_addr)),
160   [Residence] "I"(offsetof(berberis::ThreadState, residence)),
161   [OutsideGeneratedCode] "I"(berberis::kOutsideGeneratedCode),
162   [InsideGeneratedCode] "I"(berberis::kInsideGeneratedCode));
163   // clang-format on
164 }
165 
berberis_entry_ExitGeneratedCode()166 [[gnu::naked]] [[gnu::noinline]] void berberis_entry_ExitGeneratedCode() {
167   END_GENERATED_CODE("ret");
168 }
169 
berberis_entry_Stop()170 [[gnu::naked]] [[gnu::noinline]] void berberis_entry_Stop() {
171   END_GENERATED_CODE("ret");
172 }
173 
berberis_entry_NoExec()174 [[gnu::naked]] [[gnu::noinline]] void berberis_entry_NoExec() {
175   asm("unimp");
176 }
177 
berberis_entry_NotTranslated()178 [[gnu::naked]] [[gnu::noinline]] void berberis_entry_NotTranslated() {
179   // @plt is needed since the symbol is dynamically linked.
180   END_GENERATED_CODE("tail berberis_HandleNotTranslated@plt");
181 }
182 
berberis_entry_Translating()183 [[gnu::naked]] [[gnu::noinline]] void berberis_entry_Translating() {
184   asm("unimp");
185 }
186 
berberis_entry_Invalidating()187 [[gnu::naked]] [[gnu::noinline]] void berberis_entry_Invalidating() {
188   asm("unimp");
189 }
190 
berberis_entry_Wrapping()191 [[gnu::naked]] [[gnu::noinline]] void berberis_entry_Wrapping() {
192   asm("unimp");
193 }
194 
berberis_entry_HandleLiteCounterThresholdReached()195 [[gnu::naked]] [[gnu::noinline]] void berberis_entry_HandleLiteCounterThresholdReached() {
196   asm("unimp");
197 }
198 
199 }  // extern "C"
200 
201 }  // namespace berberis
202