2018-01-06 21:15:25 +00:00
|
|
|
/* This file is part of the dynarmic project.
|
|
|
|
* Copyright (c) 2018 MerryMage
|
|
|
|
* This software may be used and distributed according to the terms of the GNU
|
|
|
|
* General Public License version 2 or any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
#include <cstddef>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
namespace Dynarmic {
|
|
|
|
namespace A64 {
|
|
|
|
|
|
|
|
using VAddr = std::uint64_t;
|
|
|
|
|
2018-01-24 15:52:49 +00:00
|
|
|
using Vector = std::array<std::uint64_t, 2>;
|
|
|
|
static_assert(sizeof(Vector) == sizeof(std::uint64_t) * 2, "Vector must be 128 bits in size");
|
|
|
|
|
2018-01-13 17:54:29 +00:00
|
|
|
enum class Exception {
|
|
|
|
/// An UndefinedFault occured due to executing instruction with an unallocated encoding
|
|
|
|
UnallocatedEncoding,
|
|
|
|
/// An UndefinedFault occured due to executing instruction containing a reserved value
|
|
|
|
ReservedValue,
|
|
|
|
/// An unpredictable instruction is to be executed. Implementation-defined behaviour should now happen.
|
|
|
|
/// This behaviour is up to the user of this library to define.
|
|
|
|
/// Note: Constraints on unpredictable behaviour are specified in the ARMv8 ARM.
|
|
|
|
UnpredictableInstruction,
|
2018-01-26 18:50:41 +00:00
|
|
|
/// A WFI instruction was executed. You may now enter a low-power state.
|
|
|
|
WaitForInterrupt,
|
|
|
|
/// A WFE instruction was executed. You may now enter a low-power state if the event register is clear.
|
|
|
|
WaitForEvent,
|
|
|
|
/// A SEV instruction was executed. The event register of all PEs should be set.
|
|
|
|
SendEvent,
|
2019-05-03 10:57:26 -04:00
|
|
|
/// A SEVL instruction was executed. The event register of the current PE should be set.
|
2018-01-26 18:50:41 +00:00
|
|
|
SendEventLocal,
|
|
|
|
/// A YIELD instruction was executed.
|
|
|
|
Yield,
|
2018-09-18 21:17:55 -04:00
|
|
|
/// A BRK instruction was executed.
|
|
|
|
Breakpoint,
|
2018-01-13 17:54:29 +00:00
|
|
|
};
|
|
|
|
|
2018-02-11 22:53:46 +00:00
|
|
|
enum class DataCacheOperation {
|
|
|
|
/// DC CISW
|
|
|
|
CleanAndInvalidateBySetWay,
|
|
|
|
/// DC CIVAC
|
|
|
|
CleanAndInvalidateByVAToPoC,
|
|
|
|
/// DC CSW
|
|
|
|
CleanBySetWay,
|
|
|
|
/// DC CVAC
|
|
|
|
CleanByVAToPoC,
|
|
|
|
/// DC CVAU
|
|
|
|
CleanByVAToPoU,
|
|
|
|
/// DC CVAP
|
|
|
|
CleanByVAToPoP,
|
|
|
|
/// DC ISW
|
|
|
|
InvalidateBySetWay,
|
|
|
|
/// DC IVAC
|
2018-05-14 09:44:18 -04:00
|
|
|
InvalidateByVAToPoC,
|
2018-02-11 22:53:46 +00:00
|
|
|
/// DC ZVA
|
|
|
|
ZeroByVA,
|
|
|
|
};
|
|
|
|
|
2018-01-06 21:15:25 +00:00
|
|
|
struct UserCallbacks {
|
|
|
|
virtual ~UserCallbacks() = default;
|
|
|
|
|
|
|
|
// All reads through this callback are 4-byte aligned.
|
|
|
|
// Memory must be interpreted as little endian.
|
|
|
|
virtual std::uint32_t MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); }
|
|
|
|
|
|
|
|
// Reads through these callbacks may not be aligned.
|
|
|
|
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
|
|
|
|
virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0;
|
|
|
|
virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0;
|
|
|
|
virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0;
|
2018-01-24 15:55:59 +00:00
|
|
|
virtual Vector MemoryRead128(VAddr vaddr) = 0;
|
2018-01-06 21:15:25 +00:00
|
|
|
|
|
|
|
// Writes through these callbacks may not be aligned.
|
|
|
|
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0;
|
|
|
|
virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0;
|
|
|
|
virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0;
|
|
|
|
virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0;
|
2018-01-24 15:55:59 +00:00
|
|
|
virtual void MemoryWrite128(VAddr vaddr, Vector value) = 0;
|
2018-01-06 21:15:25 +00:00
|
|
|
|
|
|
|
// If this callback returns true, the JIT will assume MemoryRead* callbacks will always
|
|
|
|
// return the same value at any point in time for this vaddr. The JIT may use this information
|
|
|
|
// in optimizations.
|
|
|
|
// A conservative implementation that always returns false is safe.
|
|
|
|
virtual bool IsReadOnlyMemory(VAddr /* vaddr */) { return false; }
|
|
|
|
|
2019-05-24 01:37:03 -04:00
|
|
|
/// The interpreter must execute exactly num_instructions starting from PC.
|
2018-01-06 21:15:25 +00:00
|
|
|
virtual void InterpreterFallback(VAddr pc, size_t num_instructions) = 0;
|
|
|
|
|
|
|
|
// This callback is called whenever a SVC instruction is executed.
|
|
|
|
virtual void CallSVC(std::uint32_t swi) = 0;
|
|
|
|
|
2018-01-13 17:54:29 +00:00
|
|
|
virtual void ExceptionRaised(VAddr pc, Exception exception) = 0;
|
2018-02-11 22:53:46 +00:00
|
|
|
virtual void DataCacheOperationRaised(DataCacheOperation /*op*/, VAddr /*value*/) {}
|
2018-01-13 17:54:29 +00:00
|
|
|
|
2018-01-06 21:15:25 +00:00
|
|
|
// Timing-related callbacks
|
|
|
|
// ticks ticks have passed
|
|
|
|
virtual void AddTicks(std::uint64_t ticks) = 0;
|
|
|
|
// How many more ticks am I allowed to execute?
|
|
|
|
virtual std::uint64_t GetTicksRemaining() = 0;
|
2018-02-20 16:54:10 +00:00
|
|
|
// Get value in the emulated counter-timer physical count register.
|
|
|
|
virtual std::uint64_t GetCNTPCT() = 0;
|
2018-01-06 21:15:25 +00:00
|
|
|
};
|
|
|
|
|
2018-06-05 12:27:37 +01:00
|
|
|
class ExclusiveMonitor;
|
|
|
|
|
2018-01-06 21:15:25 +00:00
|
|
|
struct UserConfig {
|
|
|
|
UserCallbacks* callbacks;
|
|
|
|
|
2018-06-05 12:27:37 +01:00
|
|
|
size_t processor_id = 0;
|
|
|
|
ExclusiveMonitor* global_monitor = nullptr;
|
|
|
|
|
2018-02-11 22:53:46 +00:00
|
|
|
/// When set to true, UserCallbacks::DataCacheOperationRaised will be called when any
|
|
|
|
/// data cache instruction is executed. Notably DC ZVA will not implicitly do anything.
|
|
|
|
/// When set to false, UserCallbacks::DataCacheOperationRaised will never be called.
|
|
|
|
/// Executing DC ZVA in this mode will result in zeros being written to memory.
|
|
|
|
bool hook_data_cache_operations = false;
|
|
|
|
|
2018-08-16 09:58:34 +01:00
|
|
|
/// Counter-timer frequency register. The value of the register is not interpreted by
|
|
|
|
/// dynarmic.
|
|
|
|
std::uint32_t cntfrq_el0 = 600000000;
|
|
|
|
|
2018-02-20 16:44:13 +00:00
|
|
|
/// CTR_EL0<27:24> is log2 of the cache writeback granule in words.
|
|
|
|
/// CTR_EL0<23:20> is log2 of the exclusives reservation granule in words.
|
2019-05-24 01:37:03 -04:00
|
|
|
/// CTR_EL0<19:16> is log2 of the smallest data/unified cacheline in words.
|
2018-02-20 16:44:13 +00:00
|
|
|
/// CTR_EL0<15:14> is the level 1 instruction cache policy.
|
|
|
|
/// CTR_EL0<3:0> is log2 of the smallest instruction cacheline in words.
|
|
|
|
std::uint32_t ctr_el0 = 0x8444c004;
|
|
|
|
|
2018-02-11 22:53:46 +00:00
|
|
|
/// DCZID_EL0<3:0> is log2 of the block size in words
|
|
|
|
/// DCZID_EL0<4> is 0 if the DC ZVA instruction is permitted.
|
|
|
|
std::uint32_t dczid_el0 = 4;
|
|
|
|
|
2018-02-12 00:06:44 +00:00
|
|
|
/// Pointer to where TPIDRRO_EL0 is stored. This pointer will be inserted into
|
|
|
|
/// emitted code.
|
|
|
|
const std::uint64_t* tpidrro_el0 = nullptr;
|
|
|
|
|
2018-02-20 17:56:20 +00:00
|
|
|
/// Pointer to where TPIDR_EL0 is stored. This pointer will be inserted into
|
|
|
|
/// emitted code.
|
|
|
|
const std::uint64_t* tpidr_el0 = nullptr;
|
|
|
|
|
2018-02-12 19:49:56 +00:00
|
|
|
/// Pointer to the page table which we can use for direct page table access.
|
2018-02-12 20:49:52 +00:00
|
|
|
/// If an entry in page_table is null, the relevant memory callback will be called.
|
|
|
|
/// If page_table is nullptr, all memory accesses hit the memory callbacks.
|
2018-02-12 19:49:56 +00:00
|
|
|
void** page_table = nullptr;
|
2018-02-12 20:49:52 +00:00
|
|
|
/// Declares how many valid address bits are there in virtual addresses.
|
|
|
|
/// Determines the size of page_table. Valid values are between 12 and 64 inclusive.
|
|
|
|
/// This is only used if page_table is not nullptr.
|
|
|
|
size_t page_table_address_space_bits = 36;
|
|
|
|
/// Determines what happens if the guest accesses an entry that is off the end of the
|
|
|
|
/// page table. If true, Dynarmic will silently mirror page_table's address space. If
|
|
|
|
/// false, accessing memory outside of page_table bounds will result in a call to the
|
|
|
|
/// relevant memory callback.
|
|
|
|
/// This is only used if page_table is not nullptr.
|
|
|
|
bool silently_mirror_page_table = true;
|
2018-02-12 19:49:56 +00:00
|
|
|
|
2018-08-16 09:59:06 +01:00
|
|
|
/// This option relates to translation. Generally when we run into an unpredictable
|
|
|
|
/// instruction the ExceptionRaised callback is called. If this is true, we define
|
|
|
|
/// definite behaviour for some unpredictable instructions.
|
|
|
|
bool define_unpredictable_behaviour = false;
|
|
|
|
|
2018-09-07 21:51:42 +01:00
|
|
|
/// This enables the fast dispatcher.
|
|
|
|
bool enable_fast_dispatch = true;
|
|
|
|
|
2018-02-18 13:04:45 +00:00
|
|
|
// The below options relate to accuracy of floating-point emulation.
|
|
|
|
|
|
|
|
/// Determines how accurate NaN handling is.
|
|
|
|
enum class NaNAccuracy {
|
|
|
|
/// Results of operations with NaNs will exactly match hardware.
|
|
|
|
Accurate,
|
|
|
|
/// Behave as if FPCR.DN is always set.
|
|
|
|
AlwaysForceDefaultNaN,
|
|
|
|
/// No special handling of NaN, other than setting default NaN when FPCR.DN is set.
|
|
|
|
NoChecks,
|
|
|
|
} floating_point_nan_accuracy = NaNAccuracy::Accurate;
|
|
|
|
|
2018-01-06 21:15:25 +00:00
|
|
|
// Determines whether AddTicks and GetTicksRemaining are called.
|
|
|
|
// If false, execution will continue until soon after Jit::HaltExecution is called.
|
|
|
|
// bool enable_ticks = true; // TODO
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace A64
|
|
|
|
} // namespace Dynarmic
|