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 #ifndef BERBERIS_GUEST_STATE_GUEST_STATE_RISCV64_H_
18 #define BERBERIS_GUEST_STATE_GUEST_STATE_RISCV64_H_
19
20 #include <cstdint>
21
22 #include "berberis/base/dependent_false.h"
23 #include "berberis/base/macros.h"
24 #include "berberis/guest_state/guest_addr.h"
25
26 namespace berberis {
27
28 struct CPUState {
29 // x1 to x31.
30 uint64_t x[31];
31 // f0 to f31. We are using uint64_t because C++ may change values of NaN when they are passed from
32 // or to function and RISC-V uses NaN-boxing which would make things problematic.
33 uint64_t f[32];
34 // RISC-V has five rounding modes, while x86-64 has only four.
35 //
36 // Extra rounding mode (RMM in RISC-V documentation) is emulated but requires the use of
37 // FE_TOWARDZERO mode for correct work.
38 //
39 // Additionally RISC-V implementation is supposed to support three “illegal” rounding modes and
40 // when they are selected all instructions which use rounding mode trigger “undefined instruction”
41 // exception.
42 //
43 // For simplicity we always keep full rounding mode (3 bits) in the frm field and set host
44 // rounding mode to appropriate one.
45 //
46 // Exceptions, on the other hand, couldn't be stored here efficiently, instead we rely on the fact
47 // that x86-64 implements all five exceptions that RISC-V needs (and more).
48 uint8_t frm : 3;
49 GuestAddr insn_addr;
50 };
51
52 template <uint8_t kIndex>
GetXReg(const CPUState & state)53 inline uint64_t GetXReg(const CPUState& state) {
54 static_assert(kIndex > 0);
55 static_assert((kIndex - 1) < arraysize(state.x));
56 return state.x[kIndex - 1];
57 }
58
59 template <uint8_t kIndex>
SetXReg(CPUState & state,uint64_t val)60 inline void SetXReg(CPUState& state, uint64_t val) {
61 static_assert(kIndex > 0);
62 static_assert((kIndex - 1) < arraysize(state.x));
63 state.x[kIndex - 1] = val;
64 }
65
66 template <uint8_t kIndex>
GetFReg(const CPUState & state)67 inline uint64_t GetFReg(const CPUState& state) {
68 static_assert((kIndex) < arraysize(state.f));
69 return state.f[kIndex];
70 }
71
72 template <uint8_t kIndex>
SetFReg(CPUState & state,uint64_t val)73 inline void SetFReg(CPUState& state, uint64_t val) {
74 static_assert((kIndex) < arraysize(state.f));
75 state.f[kIndex] = val;
76 }
77
78 enum class RegisterType {
79 kReg,
80 kFpReg,
81 };
82
83 template <RegisterType register_type, uint8_t kIndex>
GetReg(const CPUState & state)84 inline auto GetReg(const CPUState& state) {
85 if constexpr (register_type == RegisterType::kReg) {
86 return GetXReg<kIndex>(state);
87 } else if constexpr (register_type == RegisterType::kFpReg) {
88 return GetFReg<kIndex>(state);
89 } else {
90 static_assert(kDependentValueFalse<register_type>, "Unsupported register type");
91 }
92 }
93
94 template <RegisterType register_type, uint8_t kIndex, typename Register>
SetReg(CPUState & state,Register val)95 inline auto SetReg(CPUState& state, Register val) {
96 if constexpr (register_type == RegisterType::kReg) {
97 return SetXReg<kIndex>(state, val);
98 } else if constexpr (register_type == RegisterType::kFpReg) {
99 return SetFReg<kIndex>(state, val);
100 } else {
101 static_assert(kDependentValueFalse<register_type>, "Unsupported register type");
102 }
103 }
104
105 struct ThreadState {
106 CPUState cpu;
107 };
108
109 } // namespace berberis
110
111 #endif // BERBERIS_GUEST_STATE_GUEST_STATE_RISCV64_H_
112