1 /*
2  * Copyright (C) 2014 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 #ifndef BERBERIS_GUEST_ABI_GUEST_PARAMS_ARCH_H_
18 #define BERBERIS_GUEST_ABI_GUEST_PARAMS_ARCH_H_
19 
20 #include <array>
21 #include <cstdint>
22 #include <type_traits>
23 
24 #include "berberis/base/bit_util.h"
25 #include "berberis/base/dependent_false.h"
26 #include "berberis/calling_conventions/calling_conventions_arm.h"
27 #include "berberis/guest_abi/guest_abi.h"
28 #include "berberis/guest_state/guest_addr.h"
29 #include "berberis/guest_state/guest_state.h"
30 
31 namespace berberis {
32 
33 class GuestVAListParams;
34 
35 class GuestParamsAndReturnHelper : protected GuestAbi {
36  protected:
37   template <typename Type,
38             GuestAbi::CallingConventionsVariant kCallingConventionsVariant,
39             typename R,
40             typename D,
41             typename S>
ParamLocationAddress(R r,D d,S s,arm::ArgLocation loc)42   constexpr static auto* ParamLocationAddress(R r, D d, S s, arm::ArgLocation loc) {
43     void* address = nullptr;
44     if (loc.kind == arm::kArgLocationStack) {
45       if constexpr (std::is_same_v<S, std::nullptr_t>) {
46         address = ToHostAddr<uint32_t>(loc.offset);
47       } else {
48         address = s + loc.offset;
49       }
50     } else if (loc.kind == arm::kArgLocationInt) {
51       if constexpr (std::is_same_v<R, std::nullptr_t>) {
52         LOG_ALWAYS_FATAL("Unsupported ArgumentKind");
53       } else {
54         address = r + loc.offset;
55       }
56     } else if (loc.kind == arm::kArgLocationSimd) {
57       if constexpr (std::is_same_v<D, std::nullptr_t>) {
58         LOG_ALWAYS_FATAL("Unsupported ArgumentKind");
59       } else {
60         address = d + loc.offset;
61       }
62     } else if (loc.kind == arm::kArgLocationIntAndStack) {
63       LOG_ALWAYS_FATAL("Arguments split between registers are stack are not currently supported");
64     } else {
65       LOG_ALWAYS_FATAL("Unknown ArgumentKind");
66     }
67 
68     static_assert(!std::is_same_v<Type, void>);
69     if constexpr (GuestArgumentInfo<Type, kCallingConventionsVariant>::kArgumentClass ==
70                   ArgumentClass::kReturnedViaIndirectPointer) {
71       return *reinterpret_cast<
72           typename GuestArgumentInfo<Type, kCallingConventionsVariant>::GuestType**>(address);
73     } else {
74       return reinterpret_cast<
75           typename GuestArgumentInfo<Type, kCallingConventionsVariant>::GuestType*>(address);
76     }
77   }
78 };
79 
80 // GuestParamsAndReturn is typesafe accessor into ThreadState.
81 // Usage looks like this:
82 //   GuestParamsAndReturn<double(int, double, int, double)> params(state);
83 //   auto x = params.Params<0>();
84 //   auto y = params.Params<1>();
85 //   params.Return() = x * y;
86 
87 template <typename, GuestAbi::CallingConventionsVariant = GuestAbi::kAapcs>
88 class GuestParamsAndReturn;  // Not defined.
89 
90 template <typename ReturnType,
91           typename... ParamType,
92           bool kNoexcept,
93           GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
94 class GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept), kCallingConventionsVariant>
95     : GuestParamsAndReturnHelper {
96  public:
GuestParamsAndReturn(ThreadState * state)97   GuestParamsAndReturn(ThreadState* state)
98       : r_(reinterpret_cast<uint32_t*>(state->cpu.r)),
99         d_(reinterpret_cast<uint32_t*>(state->cpu.d)),
100         s_(ToHostAddr<uint8_t>(state->cpu.r[13])) {}
101 
102   template <size_t index>
Params()103   auto* Params() const {
104     static_assert(index < sizeof...(ParamType));
105     return this->ParamLocationAddress<std::tuple_element_t<index, std::tuple<ParamType...>>,
106                                       kCallingConventionsVariant>(
107         r_, d_, s_, kParamsLocations[index]);
108   }
109 
Return()110   auto* Return() const {
111     return this->ParamLocationAddress<ReturnType, kCallingConventionsVariant>(
112         r_, d_, s_, kReturnLocation);
113   }
114 
115  private:
116   friend class GuestVAListParams;
117   friend class GuestParamsAndReturn<ReturnType(ParamType..., ...) noexcept(kNoexcept),
118                                     kCallingConventionsVariant>;
119 
120   constexpr static const std::tuple<arm::CallingConventions,
121                                     std::array<arm::ArgLocation, sizeof...(ParamType)>>
ParamsInfoHelper()122   ParamsInfoHelper() {
123     struct {
124       const ArgumentClass kArgumentClass;
125       const unsigned kSize;
126       const unsigned kAlignment;
127     } const kArgumentsInfo[] = {
128         {.kArgumentClass = GuestArgumentInfo<ParamType, kCallingConventionsVariant>::kArgumentClass,
129          .kSize = GuestArgumentInfo<ParamType, kCallingConventionsVariant>::kSize,
130          .kAlignment = GuestArgumentInfo<ParamType, kCallingConventionsVariant>::kAlignment}...};
131 
132     arm::CallingConventions conv;
133 
134     // Skip hidden argument if it exists.
135     if constexpr (!std::is_same_v<ReturnType, void>) {
136       if constexpr (GuestArgumentInfo<ReturnType, kCallingConventionsVariant>::kArgumentClass ==
137                     ArgumentClass::kReturnedViaIndirectPointer) {
138         conv.GetNextIntArgLoc(
139             GuestArgumentInfo<ReturnType*, kCallingConventionsVariant>::kSize,
140             GuestArgumentInfo<ReturnType*, kCallingConventionsVariant>::kAlignment);
141       }
142     }
143 
144     std::array<arm::ArgLocation, sizeof...(ParamType)> result{};
145     for (const auto& kArgInfo : kArgumentsInfo) {
146       if (kArgInfo.kArgumentClass == ArgumentClass::kInteger) {
147         result[&kArgInfo - kArgumentsInfo] =
148             conv.GetNextIntArgLoc(kArgInfo.kSize, kArgInfo.kAlignment);
149       } else if (kArgInfo.kArgumentClass == ArgumentClass::kVFP) {
150         result[&kArgInfo - kArgumentsInfo] =
151             conv.GetNextFpArgLoc(kArgInfo.kSize, kArgInfo.kAlignment);
152       } else {
153         LOG_ALWAYS_FATAL("Unsupported ArgumentClass");
154       }
155     }
156 
157     return {conv, result};
158   }
159 
ReturnInfoHelper()160   constexpr static arm::ArgLocation ReturnInfoHelper() {
161     arm::CallingConventions conv;
162     using ReturnInfo = GuestArgumentInfo<ReturnType, kCallingConventionsVariant>;
163     if constexpr (std::is_same_v<ReturnType, void>) {
164       return {arm::kArgLocationNone, 0};
165     } else if constexpr (ReturnInfo::kArgumentClass == ArgumentClass::kInteger) {
166       return conv.GetIntResLoc(ReturnInfo::kSize);
167     } else if constexpr (ReturnInfo::kArgumentClass == ArgumentClass::kVFP) {
168       return conv.GetFpResLoc(ReturnInfo::kSize);
169     } else if constexpr (ReturnInfo::kArgumentClass == ArgumentClass::kReturnedViaIndirectPointer) {
170       static_assert(GuestArgumentInfo<ReturnType*, kCallingConventionsVariant>::kArgumentClass ==
171                     ArgumentClass::kInteger);
172       return conv.GetIntResLoc(GuestArgumentInfo<ReturnType*, kCallingConventionsVariant>::kSize);
173     } else {
174       static_assert(kDependentTypeFalse<ReturnType>, "Unsupported ArgumentClass");
175     }
176   }
177 
178   constexpr static std::array<arm::ArgLocation, sizeof...(ParamType)> kParamsLocations =
179       std::get<1>(ParamsInfoHelper());
180 
181   constexpr static arm::ArgLocation kReturnLocation = ReturnInfoHelper();
182 
183   uint32_t* r_;
184   uint32_t* d_;
185   uint8_t* s_;
186 };
187 
188 // Partial specialization for GuestParamsAndReturn<FunctionToPointer> - it acts the same
189 // as the corresponding GuestParamsAndReturn<Function>.
190 template <typename ReturnType,
191           typename... ParamType,
192           bool kNoexcept,
193           GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
194 class GuestParamsAndReturn<ReturnType (*)(ParamType...) noexcept(kNoexcept),
195                            kCallingConventionsVariant>
196     : public GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept),
197                                   kCallingConventionsVariant> {
198  public:
GuestParamsAndReturn(ThreadState * state)199   GuestParamsAndReturn(ThreadState* state)
200       : GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept),
201                              kCallingConventionsVariant>(state) {}
202 };
203 
204 // Partial specialization for GuestParamsAndReturn<Function> for functions with variadic
205 // arguents - it acts the same as the corresponding one without it (but with forced kAapcs ABI).
206 // “Note: There are no VFP CPRCs in a variadic procedure” ⇦ from AAPCS
207 template <typename ReturnType,
208           typename... ParamType,
209           bool kNoexcept,
210           GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
211 class GuestParamsAndReturn<ReturnType(ParamType..., ...) noexcept(kNoexcept),
212                            kCallingConventionsVariant>
213     : public GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept),
214                                   GuestAbi::kDefaultAbi> {
215  public:
GuestParamsAndReturn(ThreadState * state)216   GuestParamsAndReturn(ThreadState* state)
217       : GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept), GuestAbi::kDefaultAbi>(
218             state) {}
219 
220  private:
221   friend class GuestVAListParams;
222   constexpr static arm::CallingConventions kVAStartBase =
223       std::get<0>(GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept),
224                                        GuestAbi::kDefaultAbi>::ParamsInfoHelper());
225 };
226 
227 // Partial specialization for GuestParamsAndReturn<FunctionToPointer> for functions with
228 // variadic arguents - it acts the same as the corresponding one without it.
229 template <typename ReturnType,
230           typename... ParamType,
231           bool kNoexcept,
232           GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
233 class GuestParamsAndReturn<ReturnType (*)(ParamType..., ...) noexcept(kNoexcept),
234                            kCallingConventionsVariant>
235     : public GuestParamsAndReturn<ReturnType(ParamType..., ...) noexcept(kNoexcept),
236                                   GuestAbi::kDefaultAbi> {
237  public:
GuestParamsAndReturn(ThreadState * state)238   GuestParamsAndReturn(ThreadState* state)
239       : GuestParamsAndReturn<ReturnType(ParamType..., ...) noexcept(kNoexcept),
240                              GuestAbi::kDefaultAbi>(state) {}
241 };
242 
243 template <typename FunctionType,
244           GuestAbi::CallingConventionsVariant kCallingConventionsVariant = GuestAbi::kDefaultAbi>
245 class GuestParamsValues;
246 
247 class GuestVAListParams : GuestParamsAndReturnHelper {
248  public:
249   template <typename Func, GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
GuestVAListParams(const GuestParamsValues<Func,kCallingConventionsVariant> && named_parameters)250   GuestVAListParams(const GuestParamsValues<Func, kCallingConventionsVariant>&& named_parameters)
251       : calling_conventions_(named_parameters.kVAStartBase,
252                              bit_cast<uint32_t>(named_parameters.s_)),
253         r_(named_parameters.r_) {}
254 
255   // Initialize with va_list which is pointer to parameters which are located
256   // as if they are passed on the stack.
GuestVAListParams(GuestAddr va)257   GuestVAListParams(GuestAddr va)
258       : calling_conventions_(arm::CallingConventions::kStackOnly, va), r_(nullptr) {}
259 
260   template <typename T>
GetParam()261   typename GuestArgumentInfo<T, GuestAbi::kDefaultAbi>::GuestType& GetParam() {
262     static_assert(GuestArgumentInfo<T, GuestAbi::kDefaultAbi>::kArgumentClass ==
263                   ArgumentClass::kInteger);
264     return *this->ParamLocationAddress<T, GuestAbi::kDefaultAbi>(
265         r_,
266         nullptr,
267         nullptr,
268         calling_conventions_.GetNextIntArgLoc(
269             GuestArgumentInfo<T, GuestAbi::kDefaultAbi>::kSize,
270             GuestArgumentInfo<T, GuestAbi::kDefaultAbi>::kAlignment));
271   }
272 
273   template <typename T>
GetPointerParam()274   T* GetPointerParam() {
275     return ToHostAddr<T>(GetParam<GuestAddr>());
276   }
277 
278  protected:
get_sp(ThreadState * state)279   static GuestAddr get_sp(ThreadState* state) { return state->cpu.r[13]; }
280 
281   arm::CallingConventions calling_conventions_;
282   uint32_t* r_;
283 };
284 
285 }  // namespace berberis
286 
287 #endif  // BERBERIS_GUEST_ABI_GUEST_PARAMS_ARCH_H_
288