/* 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 #include #include #include namespace Dynarmic { namespace A64 { using VAddr = std::uint64_t; using Vector = std::array; static_assert(sizeof(Vector) == sizeof(std::uint64_t) * 2, "Vector must be 128 bits in size"); 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, /// 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 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 YIELD instruction was executed. (Hint instruction.) Yield, /// A BRK instruction was executed. (Hint instruction.) Breakpoint, }; 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 InvalidateByVAToPoC, /// DC ZVA ZeroByVA, }; 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; virtual Vector MemoryRead128(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; virtual void MemoryWrite128(VAddr vaddr, Vector value) = 0; // 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; virtual void DataCacheOperationRaised(DataCacheOperation /*op*/, VAddr /*value*/) {} // 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; // Get value in the emulated counter-timer physical count register. virtual std::uint64_t GetCNTPCT() = 0; }; class ExclusiveMonitor; struct UserConfig { UserCallbacks* callbacks; size_t processor_id = 0; ExclusiveMonitor* global_monitor = nullptr; /// 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; /// When set to true, UserCallbacks::ExceptionRaised will be called when any hint /// instruction is executed. bool hook_hint_instructions = false; /// Counter-timer frequency register. The value of the register is not interpreted by /// dynarmic. std::uint32_t cntfrq_el0 = 600000000; /// 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. /// CTR_EL0<19:16> is log2 of the smallest data/unified cacheline in words. /// 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; /// 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; /// Pointer to where TPIDRRO_EL0 is stored. This pointer will be inserted into /// emitted code. const std::uint64_t* tpidrro_el0 = nullptr; /// Pointer to where TPIDR_EL0 is stored. This pointer will be inserted into /// emitted code. const std::uint64_t* tpidr_el0 = nullptr; /// Pointer to the page table which we can use for direct page table access. /// 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. void** page_table = nullptr; /// 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; /// 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; /// This enables the fast dispatcher. bool enable_fast_dispatch = true; // 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; // 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