mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
core: Remove usage of unicorn
Unicorn long-since lost most of its use, due to dynarmic gaining support for handling most instructions. At this point any further issues encountered should be used to make dynarmic better. This also allows us to remove our dependency on Python.
This commit is contained in:
parent
46fdc94586
commit
fc6db97a09
18 changed files with 18 additions and 520 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -7,9 +7,6 @@
|
|||
[submodule "dynarmic"]
|
||||
path = externals/dynarmic
|
||||
url = https://github.com/MerryMage/dynarmic.git
|
||||
[submodule "unicorn"]
|
||||
path = externals/unicorn
|
||||
url = https://github.com/yuzu-emu/unicorn
|
||||
[submodule "soundtouch"]
|
||||
path = externals/soundtouch
|
||||
url = https://github.com/citra-emu/ext-soundtouch.git
|
||||
|
|
|
@ -4,16 +4,8 @@ cd /yuzu
|
|||
# override Travis CI unreasonable ccache size
|
||||
echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
|
||||
|
||||
# Dirty hack to trick unicorn makefile into believing we are in a MINGW system
|
||||
mv /bin/uname /bin/uname1 && echo -e '#!/bin/sh\necho MINGW64' >> /bin/uname
|
||||
chmod +x /bin/uname
|
||||
|
||||
# Dirty hack to trick unicorn makefile into believing we have cmd
|
||||
echo '' >> /bin/cmd
|
||||
chmod +x /bin/cmd
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
|
||||
ninja
|
||||
|
||||
# Clean up the dirty hacks
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
cd /yuzu
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -G Ninja -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
|
||||
cmake .. -G Ninja -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
|
||||
ninja
|
||||
|
||||
ccache -s
|
||||
|
|
|
@ -4,13 +4,12 @@ set -o pipefail
|
|||
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.14
|
||||
export Qt5_DIR=$(brew --prefix)/opt/qt5
|
||||
export UNICORNDIR=$(pwd)/externals/unicorn
|
||||
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
# TODO: Build using ninja instead of make
|
||||
mkdir build && cd build
|
||||
cmake --version
|
||||
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
|
||||
cmake .. -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
|
||||
make -j4
|
||||
|
||||
ccache -s
|
||||
|
|
|
@ -18,8 +18,6 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "EN
|
|||
|
||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||
|
||||
option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
|
||||
|
||||
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
|
||||
|
||||
option(YUZU_ENABLE_BOXCAT "Enable the Boxcat service, a yuzu high-level implementation of BCAT" ON)
|
||||
|
@ -372,81 +370,6 @@ endif()
|
|||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# If unicorn isn't found, msvc -> download bundled unicorn; everyone else -> build external
|
||||
if (YUZU_USE_BUNDLED_UNICORN)
|
||||
if (MSVC)
|
||||
message(STATUS "unicorn not found, falling back to bundled")
|
||||
# Detect toolchain and platform
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
|
||||
set(UNICORN_VER "unicorn-yuzu")
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Unicorn binaries for your toolchain. Disable YUZU_USE_BUNDLED_UNICORN and provide your own.")
|
||||
endif()
|
||||
|
||||
if (DEFINED UNICORN_VER)
|
||||
download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
|
||||
endif()
|
||||
|
||||
if (DEFINED UNICORN_VER)
|
||||
download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
|
||||
endif()
|
||||
|
||||
set(UNICORN_FOUND YES)
|
||||
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
|
||||
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/lib/x64/unicorn_dynload.lib" CACHE PATH "Path to Unicorn library" FORCE)
|
||||
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/lib/x64/" CACHE PATH "Path to unicorn.dll" FORCE)
|
||||
else()
|
||||
message(STATUS "unicorn not found, falling back to externals")
|
||||
if (MINGW)
|
||||
set(UNICORN_LIB_NAME "unicorn.a")
|
||||
else()
|
||||
set(UNICORN_LIB_NAME "libunicorn.a")
|
||||
endif()
|
||||
|
||||
set(UNICORN_FOUND YES)
|
||||
set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn)
|
||||
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE)
|
||||
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
|
||||
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE)
|
||||
|
||||
find_package(PythonInterp 2.7 REQUIRED)
|
||||
|
||||
if (MINGW)
|
||||
# Intentionally call the unicorn makefile directly instead of using make.sh so that we can override the
|
||||
# UNAME_S makefile variable to MINGW. This way we don't have to hack at the uname binary to build
|
||||
# Additionally, overriding DO_WINDOWS_EXPORT prevents unicorn from patching the static unicorn.a by using msvc and cmd,
|
||||
# which are both things we don't have in a mingw cross compiling environment.
|
||||
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
|
||||
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-gcc-ar RANLIB=x86_64-w64-mingw32-gcc-ranlib make UNAME_S=MINGW DO_WINDOWS_EXPORT=0
|
||||
WORKING_DIRECTORY ${UNICORN_PREFIX}
|
||||
)
|
||||
else()
|
||||
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
|
||||
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no
|
||||
WORKING_DIRECTORY ${UNICORN_PREFIX}
|
||||
)
|
||||
endif()
|
||||
|
||||
# ALL makes this custom target build every time
|
||||
# but it won't actually build if LIBUNICORN_LIBRARY is up to date
|
||||
add_custom_target(unicorn-build ALL
|
||||
DEPENDS ${LIBUNICORN_LIBRARY}
|
||||
)
|
||||
unset(UNICORN_LIB_NAME)
|
||||
endif()
|
||||
else()
|
||||
find_package(Unicorn REQUIRED)
|
||||
endif()
|
||||
|
||||
if (UNICORN_FOUND)
|
||||
add_library(unicorn INTERFACE)
|
||||
add_dependencies(unicorn unicorn-build)
|
||||
target_link_libraries(unicorn INTERFACE "${LIBUNICORN_LIBRARY}")
|
||||
target_include_directories(unicorn INTERFACE "${LIBUNICORN_INCLUDE_DIR}")
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find or build unicorn which is required.")
|
||||
endif()
|
||||
|
||||
# Platform-specific library requirements
|
||||
# ======================================
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
function(copy_yuzu_unicorn_deps target_dir)
|
||||
include(WindowsCopyFiles)
|
||||
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
|
||||
windows_copy_files(${target_dir} ${UNICORN_DLL_DIR} ${DLL_DEST}
|
||||
libgcc_s_seh-1.dll
|
||||
libwinpthread-1.dll
|
||||
unicorn.dll
|
||||
)
|
||||
endfunction(copy_yuzu_unicorn_deps)
|
1
externals/unicorn
vendored
1
externals/unicorn
vendored
|
@ -1 +0,0 @@
|
|||
Subproject commit 73f45735354396766a4bfb26d0b96b06e5cf31b2
|
|
@ -13,8 +13,6 @@ add_library(core STATIC
|
|||
arm/dynarmic/arm_exclusive_monitor.h
|
||||
arm/exclusive_monitor.cpp
|
||||
arm/exclusive_monitor.h
|
||||
arm/unicorn/arm_unicorn.cpp
|
||||
arm/unicorn/arm_unicorn.h
|
||||
constants.cpp
|
||||
constants.h
|
||||
core.cpp
|
||||
|
@ -644,7 +642,7 @@ endif()
|
|||
create_target_directory_groups(core)
|
||||
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus unicorn zip)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus zip)
|
||||
|
||||
if (YUZU_ENABLE_BOXCAT)
|
||||
target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <dynarmic/A32/a32.h>
|
||||
#include <dynarmic/A32/config.h>
|
||||
#include <dynarmic/A32/context.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/page_table.h"
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <memory>
|
||||
#include <dynarmic/A64/a64.h>
|
||||
#include <dynarmic/A64/config.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/page_table.h"
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
|
@ -13,7 +14,6 @@
|
|||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
|
@ -82,16 +82,9 @@ public:
|
|||
}
|
||||
|
||||
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
|
||||
LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||
num_instructions, MemoryReadCode(pc));
|
||||
|
||||
ARM_Interface::ThreadContext64 ctx;
|
||||
parent.SaveContext(ctx);
|
||||
parent.inner_unicorn.LoadContext(ctx);
|
||||
parent.inner_unicorn.ExecuteInstructions(num_instructions);
|
||||
parent.inner_unicorn.SaveContext(ctx);
|
||||
parent.LoadContext(ctx);
|
||||
num_interpreted_instructions += num_instructions;
|
||||
LOG_ERROR(Core_ARM,
|
||||
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||
num_instructions, MemoryReadCode(pc));
|
||||
}
|
||||
|
||||
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
|
||||
|
@ -127,18 +120,17 @@ public:
|
|||
if (parent.uses_wall_clock) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
|
||||
// rough approximation of the amount of executed ticks in the system, it may be thrown off
|
||||
// if not all cores are doing a similar amount of work. Instead of doing this, we should
|
||||
// device a way so that timing is consistent across all cores without increasing the ticks 4
|
||||
// times.
|
||||
u64 amortized_ticks =
|
||||
(ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES;
|
||||
u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES;
|
||||
// Always execute at least one tick.
|
||||
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
||||
|
||||
parent.system.CoreTiming().AddTicks(amortized_ticks);
|
||||
num_interpreted_instructions = 0;
|
||||
}
|
||||
|
||||
u64 GetTicksRemaining() override {
|
||||
|
@ -156,7 +148,6 @@ public:
|
|||
}
|
||||
|
||||
ARM_Dynarmic_64& parent;
|
||||
std::size_t num_interpreted_instructions = 0;
|
||||
u64 tpidrro_el0 = 0;
|
||||
u64 tpidr_el0 = 0;
|
||||
static constexpr u64 minimum_run_cycles = 1000U;
|
||||
|
@ -248,12 +239,8 @@ ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handle
|
|||
bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor,
|
||||
std::size_t core_index)
|
||||
: ARM_Interface{system, interrupt_handlers, uses_wall_clock},
|
||||
cb(std::make_unique<DynarmicCallbacks64>(*this)), inner_unicorn{system, interrupt_handlers,
|
||||
uses_wall_clock,
|
||||
ARM_Unicorn::Arch::AArch64,
|
||||
core_index},
|
||||
core_index{core_index}, exclusive_monitor{
|
||||
dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index},
|
||||
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
|
||||
ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "common/hash.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
|
@ -71,7 +70,6 @@ private:
|
|||
std::unique_ptr<DynarmicCallbacks64> cb;
|
||||
JitCacheType jit_cache;
|
||||
std::shared_ptr<Dynarmic::A64::Jit> jit;
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
std::size_t core_index;
|
||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
|
|
|
@ -1,295 +0,0 @@
|
|||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <unicorn/arm64.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
// Load Unicorn DLL once on Windows using RAII
|
||||
#ifdef _MSC_VER
|
||||
#include <unicorn_dynload.h>
|
||||
struct LoadDll {
|
||||
private:
|
||||
LoadDll() {
|
||||
ASSERT(uc_dyn_load(NULL, 0));
|
||||
}
|
||||
~LoadDll() {
|
||||
ASSERT(uc_dyn_free());
|
||||
}
|
||||
static LoadDll g_load_dll;
|
||||
};
|
||||
LoadDll LoadDll::g_load_dll;
|
||||
#endif
|
||||
|
||||
#define CHECKED(expr) \
|
||||
do { \
|
||||
if (auto _cerr = (expr)) { \
|
||||
ASSERT_MSG(false, "Call " #expr " failed with error: {} ({})\n", _cerr, \
|
||||
uc_strerror(_cerr)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) {
|
||||
GDBStub::BreakpointAddress bkpt =
|
||||
GDBStub::GetNextBreakpointFromAddress(address, GDBStub::BreakpointType::Execute);
|
||||
if (GDBStub::IsMemoryBreak() ||
|
||||
(bkpt.type != GDBStub::BreakpointType::None && address == bkpt.address)) {
|
||||
auto core = static_cast<ARM_Unicorn*>(user_data);
|
||||
core->RecordBreak(bkpt);
|
||||
uc_emu_stop(uc);
|
||||
}
|
||||
}
|
||||
|
||||
static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
|
||||
void* user_data) {
|
||||
auto* const system = static_cast<System*>(user_data);
|
||||
|
||||
ARM_Interface::ThreadContext64 ctx{};
|
||||
system->CurrentArmInterface().SaveContext(ctx);
|
||||
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
|
||||
ctx.pc, ctx.cpu_registers[30]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ARM_Unicorn::ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
|
||||
Arch architecture, std::size_t core_index)
|
||||
: ARM_Interface{system, interrupt_handlers, uses_wall_clock}, core_index{core_index} {
|
||||
const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64;
|
||||
CHECKED(uc_open(arch, UC_MODE_ARM, &uc));
|
||||
|
||||
auto fpv = 3 << 20;
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_CPACR_EL1, &fpv));
|
||||
|
||||
uc_hook hook{};
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, UINT64_MAX));
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, &system, 0,
|
||||
UINT64_MAX));
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, UINT64_MAX));
|
||||
last_bkpt_hit = false;
|
||||
}
|
||||
}
|
||||
|
||||
ARM_Unicorn::~ARM_Unicorn() {
|
||||
CHECKED(uc_close(uc));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetPC(u64 pc) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
|
||||
}
|
||||
|
||||
u64 ARM_Unicorn::GetPC() const {
|
||||
u64 val{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &val));
|
||||
return val;
|
||||
}
|
||||
|
||||
u64 ARM_Unicorn::GetReg(int regn) const {
|
||||
u64 val{};
|
||||
auto treg = UC_ARM64_REG_SP;
|
||||
if (regn <= 28) {
|
||||
treg = (uc_arm64_reg)(UC_ARM64_REG_X0 + regn);
|
||||
} else if (regn < 31) {
|
||||
treg = (uc_arm64_reg)(UC_ARM64_REG_X29 + regn - 29);
|
||||
}
|
||||
CHECKED(uc_reg_read(uc, treg, &val));
|
||||
return val;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetReg(int regn, u64 val) {
|
||||
auto treg = UC_ARM64_REG_SP;
|
||||
if (regn <= 28) {
|
||||
treg = (uc_arm64_reg)(UC_ARM64_REG_X0 + regn);
|
||||
} else if (regn < 31) {
|
||||
treg = (uc_arm64_reg)(UC_ARM64_REG_X29 + regn - 29);
|
||||
}
|
||||
CHECKED(uc_reg_write(uc, treg, &val));
|
||||
}
|
||||
|
||||
u128 ARM_Unicorn::GetVectorReg(int /*index*/) const {
|
||||
UNIMPLEMENTED();
|
||||
static constexpr u128 res{};
|
||||
return res;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
u32 ARM_Unicorn::GetPSTATE() const {
|
||||
u64 nzcv{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv));
|
||||
return static_cast<u32>(nzcv);
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetPSTATE(u32 pstate) {
|
||||
u64 nzcv = pstate;
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv));
|
||||
}
|
||||
|
||||
VAddr ARM_Unicorn::GetTlsAddress() const {
|
||||
u64 base{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
|
||||
return base;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetTlsAddress(VAddr base) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
|
||||
}
|
||||
|
||||
u64 ARM_Unicorn::GetTPIDR_EL0() const {
|
||||
u64 value{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDR_EL0, &value));
|
||||
return value;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetTPIDR_EL0(u64 value) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::ChangeProcessorID(std::size_t new_core_id) {
|
||||
core_index = new_core_id;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Run() {
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
ExecuteInstructions(std::max(4000000U, 0U));
|
||||
} else {
|
||||
while (true) {
|
||||
if (interrupt_handlers[core_index].IsInterrupted()) {
|
||||
return;
|
||||
}
|
||||
ExecuteInstructions(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Step() {
|
||||
ExecuteInstructions(1);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
|
||||
|
||||
void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
|
||||
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
|
||||
|
||||
// Temporarily map the code page for Unicorn
|
||||
u64 map_addr{GetPC() & ~Memory::PAGE_MASK};
|
||||
std::vector<u8> page_buffer(Memory::PAGE_SIZE);
|
||||
system.Memory().ReadBlock(map_addr, page_buffer.data(), page_buffer.size());
|
||||
|
||||
CHECKED(uc_mem_map_ptr(uc, map_addr, page_buffer.size(),
|
||||
UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data()));
|
||||
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
|
||||
CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size()));
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
|
||||
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
|
||||
}
|
||||
|
||||
Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread();
|
||||
SaveContext(thread->GetContext64());
|
||||
if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
|
||||
last_bkpt_hit = false;
|
||||
GDBStub::Break();
|
||||
GDBStub::SendTrap(thread, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SaveContext(ThreadContext64& ctx) {
|
||||
int uregs[32];
|
||||
void* tregs[32];
|
||||
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp));
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc));
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
|
||||
|
||||
for (auto i = 0; i < 29; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_X0 + i;
|
||||
tregs[i] = &ctx.cpu_registers[i];
|
||||
}
|
||||
uregs[29] = UC_ARM64_REG_X29;
|
||||
tregs[29] = (void*)&ctx.cpu_registers[29];
|
||||
uregs[30] = UC_ARM64_REG_X30;
|
||||
tregs[30] = (void*)&ctx.cpu_registers[30];
|
||||
|
||||
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 31));
|
||||
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_Q0 + i;
|
||||
tregs[i] = &ctx.vector_registers[i];
|
||||
}
|
||||
|
||||
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::LoadContext(const ThreadContext64& ctx) {
|
||||
int uregs[32];
|
||||
void* tregs[32];
|
||||
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp));
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc));
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
|
||||
|
||||
for (int i = 0; i < 29; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_X0 + i;
|
||||
tregs[i] = (void*)&ctx.cpu_registers[i];
|
||||
}
|
||||
uregs[29] = UC_ARM64_REG_X29;
|
||||
tregs[29] = (void*)&ctx.cpu_registers[29];
|
||||
uregs[30] = UC_ARM64_REG_X30;
|
||||
tregs[30] = (void*)&ctx.cpu_registers[30];
|
||||
|
||||
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 31));
|
||||
|
||||
for (auto i = 0; i < 32; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_Q0 + i;
|
||||
tregs[i] = (void*)&ctx.vector_registers[i];
|
||||
}
|
||||
|
||||
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::PrepareReschedule() {
|
||||
CHECKED(uc_emu_stop(uc));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::ClearExclusiveState() {}
|
||||
|
||||
void ARM_Unicorn::ClearInstructionCache() {}
|
||||
|
||||
void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
|
||||
last_bkpt = bkpt;
|
||||
last_bkpt_hit = true;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) {
|
||||
u32 esr{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
|
||||
|
||||
const auto ec = esr >> 26;
|
||||
const auto iss = esr & 0xFFFFFF;
|
||||
|
||||
auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data);
|
||||
|
||||
switch (ec) {
|
||||
case 0x15: // SVC
|
||||
Kernel::Svc::Call(arm_instance->system, iss);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
|
@ -1,63 +0,0 @@
|
|||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class System;
|
||||
|
||||
class ARM_Unicorn final : public ARM_Interface {
|
||||
public:
|
||||
enum class Arch {
|
||||
AArch32, // 32-bit ARM
|
||||
AArch64, // 64-bit ARM
|
||||
};
|
||||
|
||||
explicit ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
|
||||
Arch architecture, std::size_t core_index);
|
||||
~ARM_Unicorn() override;
|
||||
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
void SetReg(int index, u64 value) override;
|
||||
u128 GetVectorReg(int index) const override;
|
||||
void SetVectorReg(int index, u128 value) override;
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
u64 GetTPIDR_EL0() const override;
|
||||
void ChangeProcessorID(std::size_t new_core_id) override;
|
||||
void PrepareReschedule() override;
|
||||
void ClearExclusiveState() override;
|
||||
void ExecuteInstructions(std::size_t num_instructions);
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged(Common::PageTable&, std::size_t) override {}
|
||||
void RecordBreak(GDBStub::BreakpointAddress bkpt);
|
||||
|
||||
void SaveContext(ThreadContext32& ctx) override {}
|
||||
void SaveContext(ThreadContext64& ctx) override;
|
||||
void LoadContext(const ThreadContext32& ctx) override {}
|
||||
void LoadContext(const ThreadContext64& ctx) override;
|
||||
|
||||
private:
|
||||
static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
|
||||
|
||||
uc_engine* uc{};
|
||||
GDBStub::BreakpointAddress last_bkpt{};
|
||||
bool last_bkpt_hit = false;
|
||||
std::size_t core_index;
|
||||
};
|
||||
|
||||
} // namespace Core
|
|
@ -2,30 +2,18 @@
|
|||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/spin_lock.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#endif
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler,
|
||||
Core::CPUInterruptHandler& interrupt_handler)
|
||||
: interrupt_handler{interrupt_handler}, core_index{id}, scheduler{scheduler} {
|
||||
|
||||
guard = std::make_unique<Common::SpinLock>();
|
||||
}
|
||||
: interrupt_handler{interrupt_handler},
|
||||
core_index{id}, scheduler{scheduler}, guard{std::make_unique<Common::SpinLock>()} {}
|
||||
|
||||
PhysicalCore::~PhysicalCore() = default;
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "common/logging/log.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
@ -217,8 +216,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
|
|||
} else {
|
||||
thread->tls_address = 0;
|
||||
}
|
||||
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
|
||||
// to initialize the context
|
||||
|
||||
thread->arm_interface.reset();
|
||||
if ((type_flags & THREADTYPE_HLE) == 0) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
|
@ -231,19 +229,10 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
|
|||
system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(),
|
||||
processor_id);
|
||||
}
|
||||
|
||||
#else
|
||||
if (owner_process && !owner_process->Is64BitProcess()) {
|
||||
thread->arm_interface = std::make_shared<Core::ARM_Unicorn>(
|
||||
system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch32,
|
||||
processor_id);
|
||||
} else {
|
||||
thread->arm_interface = std::make_shared<Core::ARM_Unicorn>(
|
||||
system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch64,
|
||||
processor_id);
|
||||
}
|
||||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
#error Platform not supported yet.
|
||||
#endif
|
||||
|
||||
ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
|
||||
static_cast<u32>(entry_point), static_cast<u32>(arg));
|
||||
ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
|
||||
|
|
|
@ -264,11 +264,9 @@ endif()
|
|||
if (MSVC)
|
||||
include(CopyYuzuQt5Deps)
|
||||
include(CopyYuzuSDLDeps)
|
||||
include(CopyYuzuUnicornDeps)
|
||||
include(CopyYuzuFFmpegDeps)
|
||||
copy_yuzu_Qt5_deps(yuzu)
|
||||
copy_yuzu_SDL_deps(yuzu)
|
||||
copy_yuzu_unicorn_deps(yuzu)
|
||||
copy_yuzu_FFmpeg_deps(yuzu)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -39,7 +39,5 @@ endif()
|
|||
|
||||
if (MSVC)
|
||||
include(CopyYuzuSDLDeps)
|
||||
include(CopyYuzuUnicornDeps)
|
||||
copy_yuzu_SDL_deps(yuzu-cmd)
|
||||
copy_yuzu_unicorn_deps(yuzu-cmd)
|
||||
endif()
|
||||
|
|
|
@ -28,7 +28,5 @@ endif()
|
|||
|
||||
if (MSVC)
|
||||
include(CopyYuzuSDLDeps)
|
||||
include(CopyYuzuUnicornDeps)
|
||||
copy_yuzu_SDL_deps(yuzu-tester)
|
||||
copy_yuzu_unicorn_deps(yuzu-tester)
|
||||
endif()
|
||||
|
|
Loading…
Reference in a new issue