summaryrefslogtreecommitdiffstats
path: root/src/core/arm/arm_interface.h
blob: a9d9ac09d3f4f750f4eccd6054c0e37db79b6a2d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <span>
#include <string>
#include <vector>

#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hardware_properties.h"

namespace Common {
struct PageTable;
}

namespace Kernel {
enum class VMAPermission : u8;
enum class DebugWatchpointType : u8;
struct DebugWatchpoint;
} // namespace Kernel

namespace Core {
class System;
class CPUInterruptHandler;

using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;

// NOTE: these values match the HaltReason enum in Dynarmic
enum class HaltReason : u64 {
    StepThread = 0x00000001,
    DataAbort = 0x00000004,
    BreakLoop = 0x02000000,
    SupervisorCall = 0x04000000,
    InstructionBreakpoint = 0x08000000,
    PrefetchAbort = 0x20000000,
};
DECLARE_ENUM_FLAG_OPERATORS(HaltReason);

enum class Architecture {
    Aarch32,
    Aarch64,
};

/// Generic ARMv8 CPU interface
class ARM_Interface {
public:
    YUZU_NON_COPYABLE(ARM_Interface);
    YUZU_NON_MOVEABLE(ARM_Interface);

    explicit ARM_Interface(System& system_, bool uses_wall_clock_)
        : system{system_}, uses_wall_clock{uses_wall_clock_} {}
    virtual ~ARM_Interface() = default;

    struct ThreadContext32 {
        std::array<u32, 16> cpu_registers{};
        std::array<u32, 64> extension_registers{};
        u32 cpsr{};
        u32 fpscr{};
        u32 fpexc{};
        u32 tpidr{};
    };
    // Internally within the kernel, it expects the AArch32 version of the
    // thread context to be 344 bytes in size.
    static_assert(sizeof(ThreadContext32) == 0x150);

    struct ThreadContext64 {
        std::array<u64, 31> cpu_registers{};
        u64 sp{};
        u64 pc{};
        u32 pstate{};
        std::array<u8, 4> padding{};
        std::array<u128, 32> vector_registers{};
        u32 fpcr{};
        u32 fpsr{};
        u64 tpidr{};
    };
    // Internally within the kernel, it expects the AArch64 version of the
    // thread context to be 800 bytes in size.
    static_assert(sizeof(ThreadContext64) == 0x320);

    /// Perform any backend-specific initialization.
    virtual void Initialize() {}

    /// Runs the CPU until an event happens
    void Run();

    /// Clear all instruction cache
    virtual void ClearInstructionCache() = 0;

    /**
     * Clear instruction cache range
     * @param addr Start address of the cache range to clear
     * @param size Size of the cache range to clear, starting at addr
     */
    virtual void InvalidateCacheRange(u64 addr, std::size_t size) = 0;

    /**
     * Notifies CPU emulation that the current page table has changed.
     *  @param new_page_table                 The new page table.
     *  @param new_address_space_size_in_bits The new usable size of the address space in bits.
     *                                        This can be either 32, 36, or 39 on official software.
     */
    virtual void PageTableChanged(Common::PageTable& new_page_table,
                                  std::size_t new_address_space_size_in_bits) = 0;

    /**
     * Set the Program Counter to an address
     * @param addr Address to set PC to
     */
    virtual void SetPC(u64 addr) = 0;

    /*
     * Get the current Program Counter
     * @return Returns current PC
     */
    virtual u64 GetPC() const = 0;

    /**
     * Get the current Stack Pointer
     * @return Returns current SP
     */
    virtual u64 GetSP() const = 0;

    /**
     * Get an ARM register
     * @param index Register index
     * @return Returns the value in the register
     */
    virtual u64 GetReg(int index) const = 0;

    /**
     * Set an ARM register
     * @param index Register index
     * @param value Value to set register to
     */
    virtual void SetReg(int index, u64 value) = 0;

    /**
     * Gets the value of a specified vector register.
     *
     * @param index The index of the vector register.
     * @return the value within the vector register.
     */
    virtual u128 GetVectorReg(int index) const = 0;

    /**
     * Sets a given value into a vector register.
     *
     * @param index The index of the vector register.
     * @param value The new value to place in the register.
     */
    virtual void SetVectorReg(int index, u128 value) = 0;

    /**
     * Get the current PSTATE register
     * @return Returns the value of the PSTATE register
     */
    virtual u32 GetPSTATE() const = 0;

    /**
     * Set the current PSTATE register
     * @param pstate Value to set PSTATE to
     */
    virtual void SetPSTATE(u32 pstate) = 0;

    virtual u64 GetTlsAddress() const = 0;

    virtual void SetTlsAddress(u64 address) = 0;

    /**
     * Gets the value within the TPIDR_EL0 (read/write software thread ID) register.
     *
     * @return the value within the register.
     */
    virtual u64 GetTPIDR_EL0() const = 0;

    /**
     * Sets a new value within the TPIDR_EL0 (read/write software thread ID) register.
     *
     * @param value The new value to place in the register.
     */
    virtual void SetTPIDR_EL0(u64 value) = 0;

    virtual Architecture GetArchitecture() const = 0;
    virtual void SaveContext(ThreadContext32& ctx) const = 0;
    virtual void SaveContext(ThreadContext64& ctx) const = 0;
    virtual void LoadContext(const ThreadContext32& ctx) = 0;
    virtual void LoadContext(const ThreadContext64& ctx) = 0;
    void LoadWatchpointArray(const WatchpointArray* wp);

    /// Clears the exclusive monitor's state.
    virtual void ClearExclusiveState() = 0;

    /// Signal an interrupt and ask the core to halt as soon as possible.
    virtual void SignalInterrupt() = 0;

    /// Clear a previous interrupt.
    virtual void ClearInterrupt() = 0;

    struct BacktraceEntry {
        std::string module;
        u64 address;
        u64 original_address;
        u64 offset;
        std::string name;
    };

    static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
                                                               const ThreadContext32& ctx);
    static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
                                                               const ThreadContext64& ctx);

    std::vector<BacktraceEntry> GetBacktrace() const;
    void LogBacktrace() const;

protected:
    /// System context that this ARM interface is running under.
    System& system;
    const WatchpointArray* watchpoints;
    bool uses_wall_clock;

    static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
    const Kernel::DebugWatchpoint* MatchingWatchpoint(
        u64 addr, u64 size, Kernel::DebugWatchpointType access_type) const;

    virtual HaltReason RunJit() = 0;
    virtual HaltReason StepJit() = 0;
    virtual u32 GetSvcNumber() const = 0;
    virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
    virtual void RewindBreakpointInstruction() = 0;
};

} // namespace Core