blob: f7fd01faba87f030f212189124d57413153bb024 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BERBERIS_GUEST_STATE_GUEST_STATE_RISCV64_H_
#define BERBERIS_GUEST_STATE_GUEST_STATE_RISCV64_H_
#include <atomic>
#include <cstdint>
#include "berberis/base/dependent_false.h"
#include "berberis/base/macros.h"
#include "berberis/guest_state/guest_addr.h"
namespace berberis {
struct CPUState {
// x0 to x31.
uint64_t x[32];
// f0 to f31. We are using uint64_t because C++ may change values of NaN when they are passed from
// or to function and RISC-V uses NaN-boxing which would make things problematic.
uint64_t f[32];
// RISC-V has five rounding modes, while x86-64 has only four.
//
// Extra rounding mode (RMM in RISC-V documentation) is emulated but requires the use of
// FE_TOWARDZERO mode for correct work.
//
// Additionally RISC-V implementation is supposed to support three “illegal” rounding modes and
// when they are selected all instructions which use rounding mode trigger “undefined instruction”
// exception.
//
// For simplicity we always keep full rounding mode (3 bits) in the frm field and set host
// rounding mode to appropriate one.
//
// Exceptions, on the other hand, couldn't be stored here efficiently, instead we rely on the fact
// that x86-64 implements all five exceptions that RISC-V needs (and more).
uint8_t frm : 3;
GuestAddr insn_addr;
};
template <uint8_t kIndex>
inline uint64_t GetXReg(const CPUState& state) {
static_assert(kIndex > 0);
static_assert(kIndex < arraysize(state.x));
return state.x[kIndex];
}
template <uint8_t kIndex>
inline void SetXReg(CPUState& state, uint64_t val) {
static_assert(kIndex > 0);
static_assert(kIndex < arraysize(state.x));
state.x[kIndex] = val;
}
template <uint8_t kIndex>
inline uint64_t GetFReg(const CPUState& state) {
static_assert((kIndex) < arraysize(state.f));
return state.f[kIndex];
}
template <uint8_t kIndex>
inline void SetFReg(CPUState& state, uint64_t val) {
static_assert((kIndex) < arraysize(state.f));
state.f[kIndex] = val;
}
enum class RegisterType {
kReg,
kFpReg,
};
template <RegisterType register_type, uint8_t kIndex>
inline auto GetReg(const CPUState& state) {
if constexpr (register_type == RegisterType::kReg) {
return GetXReg<kIndex>(state);
} else if constexpr (register_type == RegisterType::kFpReg) {
return GetFReg<kIndex>(state);
} else {
static_assert(kDependentValueFalse<register_type>, "Unsupported register type");
}
}
template <RegisterType register_type, uint8_t kIndex, typename Register>
inline auto SetReg(CPUState& state, Register val) {
if constexpr (register_type == RegisterType::kReg) {
return SetXReg<kIndex>(state, val);
} else if constexpr (register_type == RegisterType::kFpReg) {
return SetFReg<kIndex>(state, val);
} else {
static_assert(kDependentValueFalse<register_type>, "Unsupported register type");
}
}
class GuestThread;
// TODO(b/28058920): Refactor into GuestThread.
// Pending signals status state machine:
// disabled <-> enabled <-> enabled and pending signals present
enum PendingSignalsStatus : uint8_t {
kPendingSignalsDisabled = 0, // initial value, must be 0
kPendingSignalsEnabled,
kPendingSignalsPresent, // implies enabled
};
// Track whether we are in generated code or not.
enum GuestThreadResidence : uint8_t {
kOutsideGeneratedCode = 0,
kInsideGeneratedCode = 1,
};
struct ThreadState {
CPUState cpu;
// Guest thread pointer.
GuestThread* thread;
// Keep pending signals status here for fast checking in generated code.
// Uses enum values from PendingSignalsStatus.
// TODO(b/28058920): Refactor into GuestThread.
std::atomic<uint_least8_t> pending_signals_status;
GuestThreadResidence residence;
// Arbitrary per-thread data added by instrumentation.
void* instrument_data;
};
// TODO(b/28058920): Refactor into GuestThread.
inline bool ArePendingSignalsPresent(const ThreadState* state) {
return state->pending_signals_status.load(std::memory_order_relaxed) == kPendingSignalsPresent;
}
// The ABI names come from
// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc.
// Integer register ABI names.
constexpr uint8_t RA = 1; // Return address - caller saved.
constexpr uint8_t SP = 2; // Stack pointer - callee saved.
constexpr uint8_t GP = 3; // Global pointer.
constexpr uint8_t TP = 4; // Thread pointer.
constexpr uint8_t T0 = 5; // Temporary register 0 - caller saved.
constexpr uint8_t T1 = 6; // Temporary register 1 - caller saved.
constexpr uint8_t T2 = 7; // Temporary register 2 - caller saved.
constexpr uint8_t FP = 8; // Frame pointer - callee saved.
constexpr uint8_t S0 = 8; // Saved register 0 - callee saved.
constexpr uint8_t S1 = 9; // Saved register 1 - callee saved.
constexpr uint8_t A0 = 10; // Argument register / return value 0 - caller saved.
constexpr uint8_t A1 = 11; // Argument register / return value 1 - caller saved.
constexpr uint8_t A2 = 12; // Argument register 2 - caller saved.
constexpr uint8_t A3 = 13; // Argument register 3 - caller saved.
constexpr uint8_t A4 = 14; // Argument register 4 - caller saved.
constexpr uint8_t A5 = 15; // Argument register 5 - caller saved.
constexpr uint8_t A6 = 16; // Argument register 6 - caller saved.
constexpr uint8_t A7 = 17; // Argument register 7 - caller saved.
constexpr uint8_t S2 = 18; // Saved register 2 - callee saved.
constexpr uint8_t S3 = 19; // Saved register 3 - callee saved.
constexpr uint8_t S4 = 20; // Saved register 4 - callee saved.
constexpr uint8_t S5 = 21; // Saved register 5 - callee saved.
constexpr uint8_t S6 = 22; // Saved register 6 - callee saved.
constexpr uint8_t S7 = 23; // Saved register 7 - callee saved.
constexpr uint8_t S8 = 24; // Saved register 8 - callee saved.
constexpr uint8_t S9 = 25; // Saved register 9 - callee saved.
constexpr uint8_t S10 = 26; // Saved register 10 - callee saved.
constexpr uint8_t S11 = 27; // Saved register 11 - callee saved.
constexpr uint8_t T3 = 28; // Temporary register 3 - caller saved.
constexpr uint8_t T4 = 29; // Temporary register 4 - caller saved.
constexpr uint8_t T5 = 30; // Temporary register 5 - caller saved.
constexpr uint8_t T6 = 31; // Temporary register 6 - caller saved.
// Floating point register ABI names.
constexpr uint8_t FT0 = 0; // FP Temporary register 0 - caller saved.
constexpr uint8_t FT1 = 1; // FP Temporary register 1 - caller saved.
constexpr uint8_t FT2 = 2; // FP Temporary register 2 - caller saved.
constexpr uint8_t FT3 = 3; // FP Temporary register 3 - caller saved.
constexpr uint8_t FT4 = 4; // FP Temporary register 4 - caller saved.
constexpr uint8_t FT5 = 5; // FP Temporary register 5 - caller saved.
constexpr uint8_t FT6 = 6; // FP Temporary register 6 - caller saved.
constexpr uint8_t FT7 = 7; // FP Temporary register 7 - caller saved.
constexpr uint8_t FS0 = 8; // FP Saved register 0 - callee saved.
constexpr uint8_t FS1 = 9; // FP Saved register 1 - callee saved.
constexpr uint8_t FA0 = 10; // FP Argument register / return value 0 - caller saved.
constexpr uint8_t FA1 = 11; // FP Argument register / return value 1 - caller saved.
constexpr uint8_t FA2 = 12; // FP Argument register 2 - caller saved.
constexpr uint8_t FA3 = 13; // FP Argument register 3 - caller saved.
constexpr uint8_t FA4 = 14; // FP Argument register 4 - caller saved.
constexpr uint8_t FA5 = 15; // FP Argument register 5 - caller saved.
constexpr uint8_t FA6 = 16; // FP Argument register 6 - caller saved.
constexpr uint8_t FA7 = 17; // FP Argument register 7 - caller saved.
constexpr uint8_t FS2 = 18; // FP Saved register 2 - calle saved.
constexpr uint8_t FS3 = 19; // FP Saved register 3 - callee saved.
constexpr uint8_t FS4 = 20; // FP Saved register 4 - callee saved.
constexpr uint8_t FS5 = 21; // FP Saved register 5 - callee saved.
constexpr uint8_t FS6 = 22; // FP Saved register 6 - callee saved.
constexpr uint8_t FS7 = 23; // FP Saved register 7 - callee saved.
constexpr uint8_t FS8 = 24; // FP Saved register 8 - callee saved.
constexpr uint8_t FS9 = 25; // FP Saved register 9 - callee saved.
constexpr uint8_t FS10 = 26; // FP Saved register 10 - callee saved.
constexpr uint8_t FS11 = 27; // FP Saved register 11 - callee saved.
constexpr uint8_t FT8 = 28; // FP Temporary register 8 - caller saved.
constexpr uint8_t FT9 = 29; // FP Temporary register 9 - caller saved.
constexpr uint8_t FT10 = 30; // FP Temporary register 10 - caller saved.
constexpr uint8_t FT11 = 31; // FP Temporary register 11 - caller saved.
} // namespace berberis
#endif // BERBERIS_GUEST_STATE_GUEST_STATE_RISCV64_H_