2020-12-28 15:15:37 +00:00
|
|
|
/* This file is part of the dynarmic project.
|
|
|
|
* Copyright (c) 2016 MerryMage
|
|
|
|
* SPDX-License-Identifier: 0BSD
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
#include <cstddef>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <memory>
|
|
|
|
|
2021-02-19 01:42:57 +01:00
|
|
|
#include <dynarmic/A32/arch_version.h>
|
2020-12-28 15:15:37 +00:00
|
|
|
#include <dynarmic/optimization_flags.h>
|
|
|
|
|
|
|
|
namespace Dynarmic {
|
|
|
|
class ExclusiveMonitor;
|
|
|
|
} // namespace Dynarmic
|
|
|
|
|
|
|
|
namespace Dynarmic {
|
|
|
|
namespace A32 {
|
|
|
|
|
|
|
|
using VAddr = std::uint32_t;
|
|
|
|
|
|
|
|
class Coprocessor;
|
|
|
|
|
|
|
|
enum class Exception {
|
|
|
|
/// An UndefinedFault occured due to executing instruction with an unallocated encoding
|
|
|
|
UndefinedInstruction,
|
|
|
|
/// 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.
|
|
|
|
UnpredictableInstruction,
|
|
|
|
/// A decode error occurred when decoding this instruction. This should never happen.
|
|
|
|
DecodeError,
|
|
|
|
/// A SEV instruction was executed. The event register of all PEs should be set. (Hint instruction.)
|
|
|
|
SendEvent,
|
|
|
|
/// A SEVL instruction was executed. The event register of the current PE should be set. (Hint instruction.)
|
|
|
|
SendEventLocal,
|
|
|
|
/// A WFI instruction was executed. You may now enter a low-power state. (Hint instruction.)
|
|
|
|
WaitForInterrupt,
|
|
|
|
/// A WFE instruction was executed. You may now enter a low-power state if the event register is clear. (Hint instruction.)
|
|
|
|
WaitForEvent,
|
|
|
|
/// A YIELD instruction was executed. (Hint instruction.)
|
|
|
|
Yield,
|
|
|
|
/// A BKPT instruction was executed.
|
|
|
|
Breakpoint,
|
|
|
|
/// A PLD instruction was executed. (Hint instruction.)
|
|
|
|
PreloadData,
|
|
|
|
/// A PLDW instruction was executed. (Hint instruction.)
|
|
|
|
PreloadDataWithIntentToWrite,
|
2021-03-08 07:51:31 +01:00
|
|
|
/// A PLI instruction was executed. (Hint instruction.)
|
|
|
|
PreloadInstruction,
|
2020-12-28 15:15:37 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/// These function pointers may be inserted into compiled code.
|
|
|
|
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.
|
|
|
|
// Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT.
|
|
|
|
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;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
// Writes through these callbacks may not be aligned.
|
|
|
|
virtual bool MemoryWriteExclusive8(VAddr /*vaddr*/, std::uint8_t /*value*/, std::uint8_t /*expected*/) { return false; }
|
|
|
|
virtual bool MemoryWriteExclusive16(VAddr /*vaddr*/, std::uint16_t /*value*/, std::uint16_t /*expected*/) { return false; }
|
|
|
|
virtual bool MemoryWriteExclusive32(VAddr /*vaddr*/, std::uint32_t /*value*/, std::uint32_t /*expected*/) { return false; }
|
|
|
|
virtual bool MemoryWriteExclusive64(VAddr /*vaddr*/, std::uint64_t /*value*/, std::uint64_t /*expected*/) { return false; }
|
|
|
|
|
|
|
|
// 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; }
|
|
|
|
|
|
|
|
/// The interpreter must execute exactly num_instructions starting from PC.
|
|
|
|
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;
|
|
|
|
|
|
|
|
virtual void ExceptionRaised(VAddr pc, Exception exception) = 0;
|
|
|
|
|
2021-01-30 02:16:18 +01:00
|
|
|
virtual void InstructionSynchronizationBarrierRaised() {}
|
|
|
|
|
2020-12-28 15:15:37 +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;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct UserConfig {
|
|
|
|
UserCallbacks* callbacks;
|
|
|
|
|
|
|
|
size_t processor_id = 0;
|
|
|
|
ExclusiveMonitor* global_monitor = nullptr;
|
|
|
|
|
2021-02-19 01:42:57 +01:00
|
|
|
/// Select the architecture version to use.
|
|
|
|
/// There are minor behavioural differences between versions.
|
|
|
|
ArchVersion arch_version = ArchVersion::v8;
|
|
|
|
|
2020-12-28 15:15:37 +00:00
|
|
|
/// This selects other optimizations than can't otherwise be disabled by setting other
|
|
|
|
/// configuration options. This includes:
|
|
|
|
/// - IR optimizations
|
|
|
|
/// - Block linking optimizations
|
|
|
|
/// - RSB optimizations
|
|
|
|
/// This is intended to be used for debugging.
|
|
|
|
OptimizationFlag optimizations = all_safe_optimizations;
|
|
|
|
|
|
|
|
bool HasOptimization(OptimizationFlag f) const {
|
|
|
|
if (!unsafe_optimizations) {
|
|
|
|
f &= all_safe_optimizations;
|
|
|
|
}
|
|
|
|
return (f & optimizations) != no_optimizations;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This enables unsafe optimizations that reduce emulation accuracy in favour of speed.
|
|
|
|
/// For safety, in order to enable unsafe optimizations you have to set BOTH this flag
|
|
|
|
/// AND the appropriate flag bits above.
|
|
|
|
/// The prefered and tested mode for this library is with unsafe optimizations disabled.
|
|
|
|
bool unsafe_optimizations = false;
|
|
|
|
|
|
|
|
// Page Table
|
|
|
|
// The page table is used for faster memory access. If an entry in the table is nullptr,
|
|
|
|
// the JIT will fallback to calling the MemoryRead*/MemoryWrite* callbacks.
|
|
|
|
static constexpr std::size_t PAGE_BITS = 12;
|
|
|
|
static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS);
|
|
|
|
std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>* page_table = nullptr;
|
|
|
|
/// Determines if the pointer in the page_table shall be offseted locally or globally.
|
|
|
|
/// 'false' will access page_table[addr >> bits][addr & mask]
|
|
|
|
/// 'true' will access page_table[addr >> bits][addr]
|
|
|
|
/// Note: page_table[addr >> bits] will still be checked to verify active pages.
|
|
|
|
/// So there might be wrongly faulted pages which maps to nullptr.
|
|
|
|
/// This can be avoided by carefully allocating the memory region.
|
|
|
|
bool absolute_offset_page_table = false;
|
2020-12-30 00:04:04 +00:00
|
|
|
/// Masks out the first N bits in host pointers from the page table.
|
|
|
|
/// The intention behind this is to allow users of Dynarmic to pack attributes in the
|
|
|
|
/// same integer and update the pointer attribute pair atomically.
|
|
|
|
/// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes.
|
|
|
|
int page_table_pointer_mask_bits = 0;
|
2020-12-28 15:15:37 +00:00
|
|
|
/// Determines if we should detect memory accesses via page_table that straddle are
|
|
|
|
/// misaligned. Accesses that straddle page boundaries will fallback to the relevant
|
|
|
|
/// memory callback.
|
|
|
|
/// This value should be the required access sizes this applies to ORed together.
|
|
|
|
/// To detect any access, use: 8 | 16 | 32 | 64.
|
|
|
|
std::uint8_t detect_misaligned_access_via_page_table = 0;
|
|
|
|
/// Determines if the above option only triggers when the misalignment straddles a
|
|
|
|
/// page boundary.
|
|
|
|
bool only_detect_misalignment_via_page_table_on_page_boundary = false;
|
|
|
|
|
|
|
|
// Fastmem Pointer
|
|
|
|
// This should point to the beginning of a 4GB address space which is in arranged just like
|
|
|
|
// what you wish for emulated memory to be. If the host page faults on an address, the JIT
|
|
|
|
// will fallback to calling the MemoryRead*/MemoryWrite* callbacks.
|
|
|
|
void* fastmem_pointer = nullptr;
|
|
|
|
/// Determines if instructions that pagefault should cause recompilation of that block
|
|
|
|
/// with fastmem disabled.
|
|
|
|
bool recompile_on_fastmem_failure = true;
|
|
|
|
|
|
|
|
// Coprocessors
|
|
|
|
std::array<std::shared_ptr<Coprocessor>, 16> coprocessors{};
|
|
|
|
|
2021-01-30 02:16:18 +01:00
|
|
|
/// When set to true, UserCallbacks::InstructionSynchronizationBarrierRaised will be
|
|
|
|
/// called when an ISB instruction is executed.
|
|
|
|
/// When set to false, ISB will be treated as a NOP instruction.
|
|
|
|
bool hook_isb = false;
|
|
|
|
|
2020-12-28 15:15:37 +00:00
|
|
|
/// Hint instructions would cause ExceptionRaised to be called with the appropriate
|
|
|
|
/// argument.
|
|
|
|
bool hook_hint_instructions = false;
|
|
|
|
|
|
|
|
/// 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;
|
|
|
|
|
|
|
|
/// HACK:
|
|
|
|
/// This tells the translator a wall clock will be used, thus allowing it
|
|
|
|
/// to avoid writting certain unnecessary code only needed for cycle timers.
|
|
|
|
bool wall_clock_cntpct = false;
|
|
|
|
|
|
|
|
/// This option relates to the CPSR.E flag. Enabling this option disables modification
|
|
|
|
/// of CPSR.E by the emulated program, forcing it to 0.
|
|
|
|
/// NOTE: Calling Jit::SetCpsr with CPSR.E=1 while this option is enabled may result
|
|
|
|
/// in unusual behavior.
|
|
|
|
bool always_little_endian = false;
|
2021-04-02 20:23:55 +02:00
|
|
|
|
|
|
|
// Minimum size is about 8MiB. Maximum size is about 2GiB. Maximum size is limited by
|
|
|
|
// the maximum length of a x64 jump.
|
|
|
|
size_t code_cache_size = 256 * 1024 * 1024; // bytes
|
|
|
|
// Determines the relative size of the near and far code caches. Must be smaller than
|
|
|
|
// code_cache_size.
|
|
|
|
size_t far_code_offset = 200 * 1024 * 1024; // bytes
|
2020-12-28 15:15:37 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace A32
|
|
|
|
} // namespace Dynarmic
|