Browse Source

[core/arm] initial fastmem/nce support on variable page sizes & macOS

remotes/1785372757367212240/tmp_refs/heads/variable-page-size
Exverge 1 month ago
parent
commit
c78d2068e1
No known key found for this signature in database GPG Key ID: DAD399BCC5FB77E4
  1. 2
      CMakeLists.txt
  2. 4
      externals/cmake-modules/DetectArchitecture.cmake
  3. 4
      src/common/assert.cpp
  4. 4
      src/common/assert.h
  5. 115
      src/common/host_memory.cpp
  6. 13
      src/common/host_memory.h
  7. 8
      src/common/settings.cpp
  8. 4
      src/core/arm/dynarmic/arm_dynarmic_32.cpp
  9. 4
      src/core/arm/dynarmic/arm_dynarmic_64.cpp
  10. 95
      src/core/arm/nce/arm_nce.cpp
  11. 88
      src/core/arm/nce/arm_nce.s
  12. 7
      src/core/arm/nce/arm_nce_asm_definitions.h
  13. 92
      src/core/arm/nce/guest_context.h
  14. 12
      src/core/arm/nce/interpreter_visitor.cpp
  15. 6
      src/core/arm/nce/interpreter_visitor.h
  16. 6
      src/core/arm/nce/patcher.cpp
  17. 5
      src/core/hle/kernel/k_memory_manager.cpp
  18. 2
      src/core/hle/kernel/svc/svc_debug_string.cpp
  19. 7
      src/core/hle/service/jit/jit_context.cpp
  20. 2
      src/core/loader/deconstructed_rom_directory.cpp
  21. 2
      src/core/loader/kip.cpp
  22. 2
      src/core/loader/nro.cpp

2
CMakeLists.txt

@ -300,7 +300,7 @@ if (NOT EXISTS ${PROJECT_BINARY_DIR}/${compat_json})
file(WRITE ${PROJECT_BINARY_DIR}/${compat_json} "") file(WRITE ${PROJECT_BINARY_DIR}/${compat_json} "")
endif() endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX))
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX OR APPLE))
set(HAS_NCE 1) set(HAS_NCE 1)
add_compile_definitions(HAS_NCE=1) add_compile_definitions(HAS_NCE=1)
endif() endif()

4
externals/cmake-modules/DetectArchitecture.cmake

@ -40,8 +40,8 @@ if (CMAKE_OSX_ARCHITECTURES)
set(ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}") set(ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
# hope and pray the architecture names match # hope and pray the architecture names match
foreach(ARCH IN ${CMAKE_OSX_ARCHITECTURES})
set(ARCHITECTURE_${ARCH} 1 PARENT_SCOPE)
foreach(ARCH ${CMAKE_OSX_ARCHITECTURES})
set(ARCHITECTURE_${ARCH} 1)
add_definitions(-DARCHITECTURE_${ARCH}=1) add_definitions(-DARCHITECTURE_${ARCH}=1)
endforeach() endforeach()

4
src/common/assert.cpp

@ -22,9 +22,9 @@ void AssertFailSoftImpl() {
# elif defined(ARCHITECTURE_arm64) # elif defined(ARCHITECTURE_arm64)
__asm__ __volatile__("brk #0"); __asm__ __volatile__("brk #0");
# else # else
exit(1);
__builtin_debugtrap();
# endif # endif
#else // POSIX ^^^ _MSC_VER vvv
#else // Clang/GCC ^^^ MSVC vvv
DebugBreak(); DebugBreak();
#endif #endif
} }

4
src/common/assert.h

@ -17,8 +17,8 @@ void AssertFailSoftImpl();
[[noreturn]] void AssertFatalImpl(); [[noreturn]] void AssertFatalImpl();
// Prevents errors on old GCC... smh... // Prevents errors on old GCC... smh...
#ifdef _MSC_VER
#define YUZU_NO_INLINE __declspec(noinline)
#if defined(_MSC_VER) || defined(__clang__)
#define YUZU_NO_INLINE
#else #else
#define YUZU_NO_INLINE __attribute__((noinline)) #define YUZU_NO_INLINE __attribute__((noinline))
#endif #endif

115
src/common/host_memory.cpp

@ -26,10 +26,8 @@
#if defined(__linux__) #if defined(__linux__)
#include <sys/random.h> #include <sys/random.h>
#elif defined(__APPLE__) #elif defined(__APPLE__)
#include <sys/types.h>
#include <sys/random.h>
#include <mach/vm_map.h>
#include <mach/mach.h> #include <mach/mach.h>
#include <mach/mach_vm.h>
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__)
#include <sys/shm.h> #include <sys/shm.h>
#elif defined(__OPENORBIS__) #elif defined(__OPENORBIS__)
@ -59,6 +57,7 @@
#include "common/free_region_manager.h" #include "common/free_region_manager.h"
#include "common/host_memory.h" #include "common/host_memory.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/settings.h"
#if defined(__ANDROID__) && __ANDROID_API__ < 30 #if defined(__ANDROID__) && __ANDROID_API__ < 30
#include <sys/syscall.h> #include <sys/syscall.h>
@ -72,9 +71,6 @@ static int memfd_create(const char* name, unsigned int flags) {
namespace Common { namespace Common {
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
#ifdef _WIN32 #ifdef _WIN32
// Manually imported for MinGW compatibility // Manually imported for MinGW compatibility
@ -401,6 +397,7 @@ private:
#ifdef ARCHITECTURE_arm64 #ifdef ARCHITECTURE_arm64
#ifndef __APPLE__
static void* ChooseVirtualBase(size_t virtual_size) { static void* ChooseVirtualBase(size_t virtual_size) {
constexpr uintptr_t Map39BitSize = (1ULL << 39); constexpr uintptr_t Map39BitSize = (1ULL << 39);
constexpr uintptr_t Map36BitSize = (1ULL << 36); constexpr uintptr_t Map36BitSize = (1ULL << 36);
@ -420,10 +417,9 @@ static void* ChooseVirtualBase(size_t virtual_size) {
uintptr_t hint_address = ((rng() % range) + lower) * HugePageSize; uintptr_t hint_address = ((rng() % range) + lower) * HugePageSize;
// Try to map. // Try to map.
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
void* map_pointer = void* map_pointer =
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE, mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0);
// If we successfully mapped, we're done. // If we successfully mapped, we're done.
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) { if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
@ -441,6 +437,56 @@ static void* ChooseVirtualBase(size_t virtual_size) {
#else #else
static void* ChooseVirtualBase(size_t virtual_size) {
virtual_size -= HugePageSize; // we handle alignment on our own
// todo: does this have to be 39bit? why?
size_t cursor = 0;
while (cursor < MACH_VM_MAX_ADDRESS - virtual_size) {
u64 region = cursor;
u64 region_size = 0;
// variables we don't need but apple forces us to use anyway
u32 info_count = VM_REGION_BASIC_INFO_COUNT_64;
vm_region_basic_info_data_64_t info;
mach_port_t name;
// find the next mapped region of memory
int res = mach_vm_region(mach_task_self(), &region, &region_size, VM_REGION_BASIC_INFO_64,
reinterpret_cast<vm_region_info_t>(&info), &info_count, &name);
// the rest of the address space is unmapped, we can just allocate here
if (res == KERN_INVALID_ADDRESS) {
return mmap(reinterpret_cast<void *>(cursor), virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
}
if (res != KERN_SUCCESS) {
LOG_WARNING(HW_Memory, "Failed to check memory region: {} {}", mach_error_string(res), res);
continue;
}
// AlignUp landed in another region, continue from here
if (region <= cursor) {
cursor = AlignUp(region + region_size, HugePageSize);
continue;
}
// find the difference between this region and the last region, if it's >= virtual_size, we can use it
if (region - cursor >= virtual_size && cursor != 0) {
auto ptr = mmap(reinterpret_cast<void *>(cursor), virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (ptr != MAP_FAILED) {
return ptr;
}
}
cursor = AlignUp(region + region_size, HugePageSize);
}
return MAP_FAILED;
}
#endif // !defined(__APPLE__)
#else
static void* ChooseVirtualBase(size_t virtual_size) { static void* ChooseVirtualBase(size_t virtual_size) {
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__) #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0); void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
@ -450,7 +496,7 @@ static void* ChooseVirtualBase(size_t virtual_size) {
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
} }
#endif
#endif // ARCHITECTURE_arm64
#if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__) #if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__)
/// Most Unices don't have a portable shm_open (AIX, OpenBSD, NetBSD, Solaris 11, OpenIndiana) /// Most Unices don't have a portable shm_open (AIX, OpenBSD, NetBSD, Solaris 11, OpenIndiana)
@ -507,8 +553,6 @@ public:
{} {}
bool Init() { bool Init() {
long page_size = sysconf(_SC_PAGESIZE);
ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging", page_size);
// Backing memory initialization // Backing memory initialization
#if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__) #if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__)
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600); fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
@ -553,7 +597,6 @@ public:
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
return false; return false;
} }
// Virtual memory initialization // Virtual memory initialization
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size)); virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
if (virtual_base == MAP_FAILED) { if (virtual_base == MAP_FAILED) {
@ -697,12 +740,13 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
#else #else
// Try to allocate a fastmem arena. // Try to allocate a fastmem arena.
// The implementation will fail with std::bad_alloc on errors. // The implementation will fail with std::bad_alloc on errors.
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, HostPageSize), AlignUp(virtual_size, HostPageSize) + HugePageSize);
if (impl->Init()) { if (impl->Init()) {
backing_base = impl->backing_base; backing_base = impl->backing_base;
virtual_base = impl->virtual_base; virtual_base = impl->virtual_base;
if (virtual_base) { if (virtual_base) {
// Ensure the virtual base is aligned to the L2 block size. // Ensure the virtual base is aligned to the L2 block size.
// TODO: move this to ChooseVirtualBase and drop virtual_base_offset
virtual_base = reinterpret_cast<u8*>(Common::AlignUp(uintptr_t(virtual_base), HugePageSize)); virtual_base = reinterpret_cast<u8*>(Common::AlignUp(uintptr_t(virtual_base), HugePageSize));
virtual_base_offset = virtual_base - impl->virtual_base; virtual_base_offset = virtual_base - impl->virtual_base;
} }
@ -724,9 +768,24 @@ HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, bool separate_heap) { void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, bool separate_heap) {
#if !(defined(__OPENORBIS__) || defined(__managarm__)) #if !(defined(__OPENORBIS__) || defined(__managarm__))
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(host_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
size_t aligned_length = length;
ASSERT(virtual_offset % HostPageSize == host_offset % HostPageSize);
// todo: placeholder for now, our best bet is probably using the whole pa page for it
if (virtual_offset % HostPageSize != 0) {
// todo: use most permissive protections? or leave to Protect
LOG_WARNING(HW_Memory, "Memory address is unaligned to virtual base, surrounding pages will inherit the same permissions", HostPageSize);
auto aligned = AlignDown(virtual_offset, HostPageSize);
auto diff = virtual_offset - aligned;
assert(virtual_offset > aligned);
virtual_offset = aligned;
aligned_length = AlignUp(length + diff, HostPageSize);
ASSERT(aligned_length >= length);
}
length = aligned_length;
ASSERT(virtual_offset % HostPageSize == 0);
ASSERT(host_offset % HostPageSize == 0);
ASSERT(length % HostPageSize == 0);
ASSERT(virtual_offset + length <= virtual_size); ASSERT(virtual_offset + length <= virtual_size);
ASSERT(host_offset + length <= backing_size); ASSERT(host_offset + length <= backing_size);
if (length == 0 || !virtual_base || !impl) { if (length == 0 || !virtual_base || !impl) {
@ -738,8 +797,8 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, M
void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) { void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) {
#if !(defined(__OPENORBIS__) || defined(__managarm__)) #if !(defined(__OPENORBIS__) || defined(__managarm__))
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset % HostPageSize == 0);
ASSERT(length % HostPageSize == 0);
ASSERT(virtual_offset + length <= virtual_size); ASSERT(virtual_offset + length <= virtual_size);
if (length == 0 || !virtual_base || !impl) { if (length == 0 || !virtual_base || !impl) {
return; return;
@ -750,15 +809,25 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap)
void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) { void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
#if !(defined(__OPENORBIS__) || defined(__managarm__)) #if !(defined(__OPENORBIS__) || defined(__managarm__))
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
bool read = True(perm & MemoryPermission::Read);
bool write = True(perm & MemoryPermission::Write);
bool execute = True(perm & MemoryPermission::Execute);
if (length % HostPageSize != 0 || virtual_offset % HostPageSize != 0) {
// todo: make this actually inherit most permissive
LOG_WARNING(HW_Memory, "Memory is unaligned to page size, surrounding pages will inherit most permissive permissions");
auto aligned = AlignDown(virtual_offset, HostPageSize);
auto diff = virtual_offset - aligned;
virtual_offset = aligned;
length = AlignUp(length + diff, HostPageSize);
}
ASSERT(!(read && write && execute));
ASSERT(virtual_offset % HostPageSize == 0);
ASSERT(length % HostPageSize == 0);
ASSERT(virtual_offset + length <= virtual_size); ASSERT(virtual_offset + length <= virtual_size);
if (length == 0 || !virtual_base || !impl) { if (length == 0 || !virtual_base || !impl) {
return; return;
} }
const bool read = True(perm & MemoryPermission::Read);
const bool write = True(perm & MemoryPermission::Write);
const bool execute = True(perm & MemoryPermission::Execute);
impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
#endif #endif
} }

13
src/common/host_memory.h

@ -8,12 +8,25 @@
#include <memory> #include <memory>
#include <optional> #include <optional>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/virtual_buffer.h" #include "common/virtual_buffer.h"
namespace Common { namespace Common {
#ifndef _MSC_VER
const size_t HostPageSize = sysconf(_SC_PAGESIZE);
#else
constexpr size_t HostPageSize = 0x1000;
#endif
const size_t GuestHostAlignment = HostPageSize / 4096;
constexpr size_t HugePageSize = 0x200000;
enum class MemoryPermission : u32 { enum class MemoryPermission : u32 {
Read = 1 << 0, Read = 1 << 0,
Write = 1 << 1, Write = 1 << 1,

8
src/common/settings.cpp

@ -177,15 +177,11 @@ bool IsDMALevelSafe() {
} }
bool IsFastmemEnabled() { bool IsFastmemEnabled() {
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
if (values.cpu_accuracy.GetValue() == CpuAccuracy::Debugging)
return bool(values.cpuopt_fastmem); return bool(values.cpuopt_fastmem);
else if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe) else if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe)
return bool(values.cpuopt_unsafe_host_mmu); return bool(values.cpuopt_unsafe_host_mmu);
#if defined(__linux__) && defined(ARCHITECTURE_arm64)
// Only 4kb systems support host MMU right now
// TODO: Support this
return getpagesize() == 4096;
#elif !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__) && !defined(__FreeBSD__)
#if !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__) && !defined(__FreeBSD__)
return false; return false;
#else #else
return true; return true;

4
src/core/arm/dynarmic/arm_dynarmic_32.cpp

@ -42,12 +42,12 @@ u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) { std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32)))
return std::nullopt; return std::nullopt;
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
auto const aligned_vaddr = vaddr & ~(Dynarmic::CODE_PAGE_SIZE - 1);
if (last_code_addr != aligned_vaddr) { if (last_code_addr != aligned_vaddr) {
m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page)); m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page));
last_code_addr = aligned_vaddr; last_code_addr = aligned_vaddr;
} }
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
return cached_code_page.inst[(vaddr & (Dynarmic::CODE_PAGE_SIZE - 1)) / sizeof(u32)];
} }
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) { void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {

4
src/core/arm/dynarmic/arm_dynarmic_64.cpp

@ -45,12 +45,12 @@ Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) { std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32)))
return std::nullopt; return std::nullopt;
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
auto const aligned_vaddr = vaddr & ~(Dynarmic::CODE_PAGE_SIZE - 1);
if (last_code_addr != aligned_vaddr) { if (last_code_addr != aligned_vaddr) {
m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page)); m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page));
last_code_addr = aligned_vaddr; last_code_addr = aligned_vaddr;
} }
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
return cached_code_page.inst[(vaddr & (Dynarmic::CODE_PAGE_SIZE - 1)) / sizeof(u32)];
} }
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) { void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {

95
src/core/arm/nce/arm_nce.cpp

@ -16,9 +16,7 @@
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include <signal.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h>
namespace Core { namespace Core {
@ -33,14 +31,6 @@ static_assert(offsetof(NativeExecutionParameters, native_context) == TpidrEl0Nat
static_assert(offsetof(NativeExecutionParameters, lock) == TpidrEl0Lock); static_assert(offsetof(NativeExecutionParameters, lock) == TpidrEl0Lock);
static_assert(offsetof(NativeExecutionParameters, magic) == TpidrEl0TlsMagic); static_assert(offsetof(NativeExecutionParameters, magic) == TpidrEl0TlsMagic);
fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
_aarch64_ctx* header = reinterpret_cast<_aarch64_ctx*>(&host_ctx.__reserved);
while (header->magic != FPSIMD_MAGIC) {
header = reinterpret_cast<_aarch64_ctx*>(reinterpret_cast<char*>(header) + header->size);
}
return reinterpret_cast<fpsimd_context*>(header);
}
using namespace Common::Literals; using namespace Common::Literals;
constexpr u32 StackSize = 128_KiB; constexpr u32 StackSize = 128_KiB;
@ -48,32 +38,29 @@ constexpr u32 StackSize = 128_KiB;
void* ArmNce::RestoreGuestContext(void* raw_context) { void* ArmNce::RestoreGuestContext(void* raw_context) {
// Retrieve the host context. // Retrieve the host context.
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
auto host_ctx = KernelContext(&static_cast<ucontext_t*>(raw_context)->uc_mcontext);
// Thread-local parameters will be located in x9. // Thread-local parameters will be located in x9.
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs[9]);
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs()[9]);
auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context); auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context);
// Retrieve the host floating point state.
auto* fpctx = GetFloatingPointState(host_ctx);
// Save host callee-saved registers. // Save host callee-saved registers.
std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &fpctx->vregs[8],
std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &host_ctx.vregs()[8],
sizeof(guest_ctx->host_ctx.host_saved_vregs)); sizeof(guest_ctx->host_ctx.host_saved_vregs));
std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &host_ctx.regs[19],
std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &host_ctx.regs()[19],
sizeof(guest_ctx->host_ctx.host_saved_regs)); sizeof(guest_ctx->host_ctx.host_saved_regs));
// Save stack pointer. // Save stack pointer.
guest_ctx->host_ctx.host_sp = host_ctx.sp;
guest_ctx->host_ctx.host_sp = *host_ctx.sp();
// Restore all guest state except tpidr_el0. // Restore all guest state except tpidr_el0.
host_ctx.sp = guest_ctx->sp;
host_ctx.pc = guest_ctx->pc;
host_ctx.pstate = guest_ctx->pstate;
fpctx->fpcr = guest_ctx->fpcr;
fpctx->fpsr = guest_ctx->fpsr;
std::memcpy(host_ctx.regs, guest_ctx->cpu_registers.data(), sizeof(host_ctx.regs));
std::memcpy(fpctx->vregs, guest_ctx->vector_registers.data(), sizeof(fpctx->vregs));
*host_ctx.sp() = guest_ctx->sp;
*host_ctx.pc() = guest_ctx->pc;
*host_ctx.pstate() = guest_ctx->pstate;
*host_ctx.fpcr() = guest_ctx->fpcr;
*host_ctx.fpsr() = guest_ctx->fpsr;
std::memcpy(host_ctx.regs(), guest_ctx->cpu_registers.data(), sizeof(guest_ctx->cpu_registers));
std::memcpy(host_ctx.vregs(), guest_ctx->vector_registers.data(), sizeof(guest_ctx->vector_registers));
// Return the new thread-local storage pointer. // Return the new thread-local storage pointer.
return tpidr; return tpidr;
@ -81,47 +68,44 @@ void* ArmNce::RestoreGuestContext(void* raw_context) {
void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) { void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) {
// Retrieve the host context. // Retrieve the host context.
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
// Retrieve the host floating point state.
auto* fpctx = GetFloatingPointState(host_ctx);
auto host_ctx = KernelContext(&static_cast<ucontext_t*>(raw_context)->uc_mcontext);
// Save all guest registers except tpidr_el0. // Save all guest registers except tpidr_el0.
std::memcpy(guest_ctx->cpu_registers.data(), host_ctx.regs, sizeof(host_ctx.regs));
std::memcpy(guest_ctx->vector_registers.data(), fpctx->vregs, sizeof(fpctx->vregs));
guest_ctx->fpsr = fpctx->fpsr;
guest_ctx->fpcr = fpctx->fpcr;
guest_ctx->pstate = static_cast<u32>(host_ctx.pstate);
guest_ctx->pc = host_ctx.pc;
guest_ctx->sp = host_ctx.sp;
std::memcpy(guest_ctx->cpu_registers.data(), host_ctx.regs(), sizeof(guest_ctx->cpu_registers));
std::memcpy(guest_ctx->vector_registers.data(), host_ctx.vregs(), sizeof(guest_ctx->vector_registers));
guest_ctx->fpsr = *host_ctx.fpsr();
guest_ctx->fpcr = *host_ctx.fpcr();
guest_ctx->pstate = *host_ctx.pstate();
guest_ctx->pc = *host_ctx.pc();
guest_ctx->sp = *host_ctx.sp();
// Restore stack pointer. // Restore stack pointer.
host_ctx.sp = guest_ctx->host_ctx.host_sp;
*host_ctx.sp() = guest_ctx->host_ctx.host_sp;
// Restore host callee-saved registers. // Restore host callee-saved registers.
std::memcpy(&host_ctx.regs[19], guest_ctx->host_ctx.host_saved_regs.data(),
std::memcpy(&host_ctx.regs()[19], guest_ctx->host_ctx.host_saved_regs.data(),
sizeof(guest_ctx->host_ctx.host_saved_regs)); sizeof(guest_ctx->host_ctx.host_saved_regs));
std::memcpy(&fpctx->vregs[8], guest_ctx->host_ctx.host_saved_vregs.data(),
std::memcpy(&host_ctx.vregs()[8], guest_ctx->host_ctx.host_saved_vregs.data(),
sizeof(guest_ctx->host_ctx.host_saved_vregs)); sizeof(guest_ctx->host_ctx.host_saved_vregs));
// Return from the call on exit by setting pc to x30. // Return from the call on exit by setting pc to x30.
host_ctx.pc = guest_ctx->host_ctx.host_saved_regs[11];
*host_ctx.pc() = guest_ctx->host_ctx.host_saved_regs[11];
// Clear esr_el1 and return it. // Clear esr_el1 and return it.
host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
host_ctx.regs()[0] = guest_ctx->esr_el1.exchange(0);
} }
bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) { bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
auto host_ctx = KernelContext(&static_cast<ucontext_t*>(raw_context)->uc_mcontext);
auto* info = static_cast<siginfo_t*>(raw_info); auto* info = static_cast<siginfo_t*>(raw_info);
// We can't handle the access, so determine why we crashed. // We can't handle the access, so determine why we crashed.
const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
const bool is_prefetch_abort = *host_ctx.pc() == reinterpret_cast<u64>(info->si_addr);
// For data aborts, skip the instruction and return to guest code. // For data aborts, skip the instruction and return to guest code.
// This will allow games to continue in many scenarios where they would otherwise crash. // This will allow games to continue in many scenarios where they would otherwise crash.
if (!is_prefetch_abort) { if (!is_prefetch_abort) {
host_ctx.pc += 4;
*host_ctx.pc() += 4;
return true; return true;
} }
@ -142,14 +126,13 @@ bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, voi
} }
bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) { bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
auto* fpctx = GetFloatingPointState(host_ctx);
auto host_ctx = KernelContext(&static_cast<ucontext_t*>(raw_context)->uc_mcontext);
auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory(); auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory();
// Match and execute an instruction. // Match and execute an instruction.
auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx, fpctx);
auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx);
if (next_pc) { if (next_pc) {
host_ctx.pc = *next_pc;
*host_ctx.pc() = *next_pc;
return true; return true;
} }
@ -278,7 +261,11 @@ ArmNce::~ArmNce() = default;
void ArmNce::Initialize() { void ArmNce::Initialize() {
if (m_thread_id == -1) { if (m_thread_id == -1) {
#if defined(__linux__)
m_thread_id = gettid(); m_thread_id = gettid();
#else
m_thread_id = pthread_mach_thread_np(pthread_self());
#endif
} }
// Configure signal stack. // Configure signal stack.
@ -381,14 +368,24 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
if (params->is_running) { if (params->is_running) {
// We should signal to the running thread. // We should signal to the running thread.
// The running thread will unlock the thread context. // The running thread will unlock the thread context.
#if defined(__linux__)
syscall(SYS_tkill, m_thread_id, BreakFromRunCodeSignal); syscall(SYS_tkill, m_thread_id, BreakFromRunCodeSignal);
#elif defined(TARGET_OS_MAC) && defined(__aarch64__)
asm volatile(
"mov x0, %0\n" // m_thread_id
"mov x1, %1\n" // BreakFromRunCodeSignal
"mov x16, #328\n" // syscall code for __pthread_kill
"svc #0x80\n"
:: "r"(static_cast<u64>(m_thread_id)), "r"(static_cast<u64>(BreakFromRunCodeSignal))
: "x0", "x1", "x16", "memory", "cc");
#endif
} else { } else {
// If the thread is no longer running, we have nothing to do. // If the thread is no longer running, we have nothing to do.
UnlockThreadParameters(params); UnlockThreadParameters(params);
} }
} }
[[maybe_unused]] const std::size_t CACHE_PAGE_SIZE = 4096;
[[maybe_unused]] const std::size_t CACHE_PAGE_SIZE = Common::HostPageSize;
void ArmNce::ClearInstructionCache() { void ArmNce::ClearInstructionCache() {
#ifdef __aarch64__ #ifdef __aarch64__

88
src/core/arm/nce/arm_nce.s

@ -7,12 +7,35 @@
mov reg, #(((val) >> 0x00) & 0xFFFF); \ mov reg, #(((val) >> 0x00) & 0xFFFF); \
movk reg, #(((val) >> 0x10) & 0xFFFF), lsl #16 movk reg, #(((val) >> 0x10) & 0xFFFF), lsl #16
#ifdef __APPLE__
#define func(name) _##name
.macro ASM_FUNCTION_START name
.text
.align 2
.global _\name
_\name:
.endm
#else
#define func(name) name
.macro ASM_FUNCTION_START name
.section .text.\name, "ax", %progbits
.global \name
.type \name, %function
\name:
.endm
#endif
/* static HaltReason Core::ArmNce::ReturnToRunCodeByTrampoline(void* tpidr, Core::GuestContext* ctx, u64 trampoline_addr) */ /* static HaltReason Core::ArmNce::ReturnToRunCodeByTrampoline(void* tpidr, Core::GuestContext* ctx, u64 trampoline_addr) */
.section .text._ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, "ax", %progbits
.global _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm
.type _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, %function
_ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm:
#ifndef __APPLE__
ASM_FUNCTION_START _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm
#else
ASM_FUNCTION_START _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEy
#endif
/* Back up host sp to x3. */ /* Back up host sp to x3. */
/* Back up host tpidr_el0 to x4. */ /* Back up host tpidr_el0 to x4. */
mov x3, sp mov x3, sp
@ -50,10 +73,7 @@ _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm:
/* static HaltReason Core::ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr) */ /* static HaltReason Core::ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr) */
.section .text._ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv, "ax", %progbits
.global _ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv
.type _ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv, %function
_ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv:
ASM_FUNCTION_START _ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv
/* This jumps to the signal handler, which will restore the entire context. */ /* This jumps to the signal handler, which will restore the entire context. */
/* On entry, x0 = thread id, which is already in the right place. */ /* On entry, x0 = thread id, which is already in the right place. */
@ -61,27 +81,30 @@ _ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv:
mov x9, x1 mov x9, x1
/* Set up arguments. */ /* Set up arguments. */
mov x8, #(__NR_tkill)
/* On entry, x0 = thread id, which is already in the right place. */
mov x1, #(ReturnToRunCodeByExceptionLevelChangeSignal) mov x1, #(ReturnToRunCodeByExceptionLevelChangeSignal)
/* Tail call the signal handler. */ /* Tail call the signal handler. */
#ifndef __APPLE__
mov x8, #(__NR_tkill)
svc #0 svc #0
#else
mov x16, #328
svc #0x80
#endif
/* Block execution from flowing here. */ /* Block execution from flowing here. */
brk #1000 brk #1000
/* static void Core::ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info, void* raw_context) */ /* static void Core::ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, %function
_ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
ASM_FUNCTION_START _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_
stp x29, x30, [sp, #-0x10]! stp x29, x30, [sp, #-0x10]!
mov x29, sp mov x29, sp
/* Call the context restorer with the raw context. */ /* Call the context restorer with the raw context. */
mov x0, x2 mov x0, x2
bl _ZN4Core6ArmNce19RestoreGuestContextEPv
bl func(_ZN4Core6ArmNce19RestoreGuestContextEPv)
/* Save the old value of tpidr_el0. */ /* Save the old value of tpidr_el0. */
mrs x8, tpidr_el0 mrs x8, tpidr_el0
@ -92,7 +115,7 @@ _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
msr tpidr_el0, x0 msr tpidr_el0, x0
/* Unlock the context. */ /* Unlock the context. */
bl _ZN4Core6ArmNce22UnlockThreadParametersEPv
bl func(_ZN4Core6ArmNce22UnlockThreadParametersEPv)
/* Returning from here will enter the guest. */ /* Returning from here will enter the guest. */
ldp x29, x30, [sp], #0x10 ldp x29, x30, [sp], #0x10
@ -100,10 +123,7 @@ _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
/* static void Core::ArmNce::BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context) */ /* static void Core::ArmNce::BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_, %function
_ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
ASM_FUNCTION_START _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_
/* Check to see if we have the correct TLS magic. */ /* Check to see if we have the correct TLS magic. */
mrs x8, tpidr_el0 mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)] ldr w9, [x8, #(TpidrEl0TlsMagic)]
@ -121,7 +141,7 @@ _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
/* Tail call the restorer. */ /* Tail call the restorer. */
mov x1, x2 mov x1, x2
b _ZN4Core6ArmNce16SaveGuestContextEPNS_12GuestContextEPv
b func(_ZN4Core6ArmNce16SaveGuestContextEPNS_12GuestContextEPv)
/* Returning from here will enter host code. */ /* Returning from here will enter host code. */
@ -131,10 +151,7 @@ _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
/* static void Core::ArmNce::GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context) */ /* static void Core::ArmNce::GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, %function
_ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
ASM_FUNCTION_START _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_
/* Check to see if we have the correct TLS magic. */ /* Check to see if we have the correct TLS magic. */
mrs x8, tpidr_el0 mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)] ldr w9, [x8, #(TpidrEl0TlsMagic)]
@ -146,7 +163,7 @@ _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
/* Incorrect TLS magic, so this is a host fault. */ /* Incorrect TLS magic, so this is a host fault. */
/* Tail call the handler. */ /* Tail call the handler. */
b _ZN4Core6ArmNce24HandleHostAlignmentFaultEiPvS1_
b func(_ZN4Core6ArmNce24HandleHostAlignmentFaultEiPvS1_)
1: 1:
/* Correct TLS magic, so this is a guest fault. */ /* Correct TLS magic, so this is a guest fault. */
@ -163,7 +180,7 @@ _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
msr tpidr_el0, x3 msr tpidr_el0, x3
/* Call the handler. */ /* Call the handler. */
bl _ZN4Core6ArmNce25HandleGuestAlignmentFaultEPNS_12GuestContextEPvS3_
bl func(_ZN4Core6ArmNce25HandleGuestAlignmentFaultEPNS_12GuestContextEPvS3_)
/* If the handler returned false, we want to preserve the host tpidr_el0. */ /* If the handler returned false, we want to preserve the host tpidr_el0. */
cbz x0, 2f cbz x0, 2f
@ -177,10 +194,7 @@ _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
ret ret
/* static void Core::ArmNce::GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context) */ /* static void Core::ArmNce::GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, %function
_ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
ASM_FUNCTION_START _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_
/* Check to see if we have the correct TLS magic. */ /* Check to see if we have the correct TLS magic. */
mrs x8, tpidr_el0 mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)] ldr w9, [x8, #(TpidrEl0TlsMagic)]
@ -192,7 +206,7 @@ _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
/* Incorrect TLS magic, so this is a host fault. */ /* Incorrect TLS magic, so this is a host fault. */
/* Tail call the handler. */ /* Tail call the handler. */
b _ZN4Core6ArmNce21HandleHostAccessFaultEiPvS1_
b func(_ZN4Core6ArmNce21HandleHostAccessFaultEiPvS1_)
1: 1:
/* Correct TLS magic, so this is a guest fault. */ /* Correct TLS magic, so this is a guest fault. */
@ -209,7 +223,7 @@ _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
msr tpidr_el0, x3 msr tpidr_el0, x3
/* Call the handler. */ /* Call the handler. */
bl _ZN4Core6ArmNce22HandleGuestAccessFaultEPNS_12GuestContextEPvS3_
bl func(_ZN4Core6ArmNce22HandleGuestAccessFaultEPNS_12GuestContextEPvS3_)
/* If the handler returned false, we want to preserve the host tpidr_el0. */ /* If the handler returned false, we want to preserve the host tpidr_el0. */
cbz x0, 2f cbz x0, 2f
@ -224,10 +238,7 @@ _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
/* static void Core::ArmNce::LockThreadParameters(void* tpidr) */ /* static void Core::ArmNce::LockThreadParameters(void* tpidr) */
.section .text._ZN4Core6ArmNce20LockThreadParametersEPv, "ax", %progbits
.global _ZN4Core6ArmNce20LockThreadParametersEPv
.type _ZN4Core6ArmNce20LockThreadParametersEPv, %function
_ZN4Core6ArmNce20LockThreadParametersEPv:
ASM_FUNCTION_START _ZN4Core6ArmNce20LockThreadParametersEPv
/* Offset to lock member. */ /* Offset to lock member. */
add x0, x0, #(TpidrEl0Lock) add x0, x0, #(TpidrEl0Lock)
@ -252,10 +263,7 @@ _ZN4Core6ArmNce20LockThreadParametersEPv:
/* static void Core::ArmNce::UnlockThreadParameters(void* tpidr) */ /* static void Core::ArmNce::UnlockThreadParameters(void* tpidr) */
.section .text._ZN4Core6ArmNce22UnlockThreadParametersEPv, "ax", %progbits
.global _ZN4Core6ArmNce22UnlockThreadParametersEPv
.type _ZN4Core6ArmNce22UnlockThreadParametersEPv, %function
_ZN4Core6ArmNce22UnlockThreadParametersEPv:
ASM_FUNCTION_START _ZN4Core6ArmNce22UnlockThreadParametersEPv
/* Offset to lock member. */ /* Offset to lock member. */
add x0, x0, #(TpidrEl0Lock) add x0, x0, #(TpidrEl0Lock)

7
src/core/arm/nce/arm_nce_asm_definitions.h

@ -5,8 +5,15 @@
#define __ASSEMBLY__ #define __ASSEMBLY__
#ifndef __APPLE__
#include <asm-generic/signal.h> #include <asm-generic/signal.h>
#include <asm-generic/unistd.h> #include <asm-generic/unistd.h>
#else
#define SIGUSR2 31
#define SIGURG 16
#define SIGSEGV 11
#define SIGBUS 10
#endif
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2 #define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
#define BreakFromRunCodeSignal SIGURG #define BreakFromRunCodeSignal SIGURG

92
src/core/arm/nce/guest_context.h

@ -10,6 +10,9 @@
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/nce/arm_nce_asm_definitions.h" #include "core/arm/nce/arm_nce_asm_definitions.h"
#include <unistd.h>
#include <signal.h>
namespace Core { namespace Core {
class ArmNce; class ArmNce;
@ -40,6 +43,90 @@ struct GuestContext {
ArmNce* parent{}; ArmNce* parent{};
}; };
class KernelContext {
public:
#if defined(__linux__)
KernelContext(void* ptr_) : ptr(static_cast<mcontext_t *>(ptr_)), fpsimd{GetFloatingPointState(ptr)} {}
u64* pc() {
// u64 (unsigned long) does not equal unsigned long long
// thank you gcc
return reinterpret_cast<u64*>(&ptr->pc);
}
u64* sp() {
return reinterpret_cast<u64*>(&ptr->sp);
}
u64* regs() {
return reinterpret_cast<u64*>(&ptr->regs);
}
u128* vregs() {
// returns __uint128, u128 is a std::array
return reinterpret_cast<u128*>(&fpsimd->vregs);
}
u32* fpcr() {
return &fpsimd->fpcr;
}
u32* fpsr() {
return &fpsimd->fpsr;
}
u32* pstate() {
// only first 32 bits are used
return reinterpret_cast<u32*>(&ptr->pstate);
}
#elif defined(__APPLE__)
KernelContext(void* ptr) : ptr(static_cast<mcontext_t *>(ptr)) {}
u64* pc() {
return &(*ptr)->__ss.__pc;
}
u64* sp() {
return &(*ptr)->__ss.__sp;
}
u64* regs() {
return (*ptr)->__ss.__x;
}
u128* vregs() {
// .__v returns __uint128, u128 is an std::array
return reinterpret_cast<u128 *>((*ptr)->__ns.__v);
}
u32* fpcr() {
return &(*ptr)->__ns.__fpcr;
}
u32* fpsr() {
return &(*ptr)->__ns.__fpsr;
}
u32* pstate() {
return &(*ptr)->__ss.__cpsr;
}
#endif
private:
mcontext_t* ptr;
#ifdef __linux__
fpsimd_context* fpsimd;
fpsimd_context* GetFloatingPointState(mcontext_t* host_ctx) {
_aarch64_ctx* header = reinterpret_cast<_aarch64_ctx*>(&host_ctx->__reserved);
while (header->magic != FPSIMD_MAGIC) {
header = reinterpret_cast<_aarch64_ctx*>(reinterpret_cast<char*>(header) + header->size);
}
return reinterpret_cast<fpsimd_context*>(header);
}
#endif
};
// Verify assembly offsets. // Verify assembly offsets.
static_assert(offsetof(GuestContext, sp) == GuestContextSp); static_assert(offsetof(GuestContext, sp) == GuestContextSp);
static_assert(offsetof(GuestContext, host_ctx) == GuestContextHostContext); static_assert(offsetof(GuestContext, host_ctx) == GuestContextHostContext);
@ -49,4 +136,9 @@ static_assert(offsetof(HostContext, host_tpidr_el0) == HostContextTpidrEl0);
static_assert(offsetof(HostContext, host_saved_regs) == HostContextRegs); static_assert(offsetof(HostContext, host_saved_regs) == HostContextRegs);
static_assert(offsetof(HostContext, host_saved_vregs) == HostContextVregs); static_assert(offsetof(HostContext, host_saved_vregs) == HostContextVregs);
#ifdef TARGET_OS_MAC
// ensure that fp and lr are next to the rest of the x registers so they can be accessed like an array
static_assert(offsetof(_STRUCT_ARM_THREAD_STATE64, __sp) - offsetof(_STRUCT_ARM_THREAD_STATE64, __x) == sizeof(u64) * 31);
#endif
} // namespace Core } // namespace Core

12
src/core/arm/nce/interpreter_visitor.cpp

@ -8,6 +8,8 @@
#include <numeric> #include <numeric>
#include "core/arm/nce/interpreter_visitor.h" #include "core/arm/nce/interpreter_visitor.h"
#include "guest_context.h"
namespace Core { namespace Core {
namespace { namespace {
@ -761,11 +763,11 @@ bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3
return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt); return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
} }
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context, fpsimd_context* fpsimd_context) {
std::span<u64, 31> regs(reinterpret_cast<u64*>(context->regs), 31);
std::span<u128, 32> vregs(reinterpret_cast<u128*>(fpsimd_context->vregs), 32);
u64& sp = *reinterpret_cast<u64*>(&context->sp);
const u64& pc = *reinterpret_cast<u64*>(&context->pc);
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, KernelContext context) {
std::span<u64, 31> regs((context.regs()), 31);
std::span<u128, 32> vregs((context.vregs()), 32);
u64& sp = *reinterpret_cast<u64*>(context.sp());
const u64& pc = *reinterpret_cast<u64*>(context.pc());
InterpreterVisitor visitor(memory, regs, vregs, sp, pc); InterpreterVisitor visitor(memory, regs, vregs, sp, pc);
u32 instruction = memory.Read32(pc); u32 instruction = memory.Read32(pc);

6
src/core/arm/nce/interpreter_visitor.h

@ -18,8 +18,9 @@
#include "core/arm/nce/visitor_base.h" #include "core/arm/nce/visitor_base.h"
namespace Core { namespace Core {
class KernelContext;
namespace Memory {
namespace Memory {
class Memory; class Memory;
} }
@ -105,7 +106,6 @@ private:
const u64& m_pc; const u64& m_pc;
}; };
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
fpsimd_context* fpsimd_context);
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, KernelContext context);
} // namespace Core } // namespace Core

6
src/core/arm/nce/patcher.cpp

@ -10,6 +10,8 @@
#include "core/arm/nce/guest_context.h" #include "core/arm/nce/guest_context.h"
#include "core/arm/nce/instructions.h" #include "core/arm/nce/instructions.h"
#include "core/arm/nce/patcher.h" #include "core/arm/nce/patcher.h"
#include "common/host_memory.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc.h"
@ -356,11 +358,11 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::Co
} }
size_t Patcher::GetSectionSize() const noexcept { size_t Patcher::GetSectionSize() const noexcept {
return Common::AlignUp(m_patch_instructions.size() * sizeof(u32), Core::Memory::YUZU_PAGESIZE);
return Common::AlignUp(m_patch_instructions.size() * sizeof(u32), Common::HostPageSize);
} }
size_t Patcher::GetPreSectionSize() const noexcept { size_t Patcher::GetPreSectionSize() const noexcept {
return Common::AlignUp(m_patch_instructions_pre.size() * sizeof(u32), Core::Memory::YUZU_PAGESIZE);
return Common::AlignUp(m_patch_instructions_pre.size() * sizeof(u32), Common::HostPageSize);
} }
void Patcher::WriteLoadContext(oaknut::VectorCodeGenerator& cg) { void Patcher::WriteLoadContext(oaknut::VectorCodeGenerator& cg) {

5
src/core/hle/kernel/k_memory_manager.cpp

@ -212,6 +212,11 @@ KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, siz
return 0; return 0;
} }
// todo: find a better way to do this
if (align_pages % Common::GuestHostAlignment != 0) {
align_pages = Common::AlignUp(align_pages, Common::GuestHostAlignment);
}
// Lock the pool that we're allocating from. // Lock the pool that we're allocating from.
const auto [pool, dir] = DecodeOption(option); const auto [pool, dir] = DecodeOption(option);
KScopedLightLock lk(m_pool_locks[static_cast<std::size_t>(pool)]); KScopedLightLock lk(m_pool_locks[static_cast<std::size_t>(pool)]);

2
src/core/hle/kernel/svc/svc_debug_string.cpp

@ -50,7 +50,7 @@ Result OutputDebugString(Core::System& system, u64 address, u64 len) {
if (flusher_data.msg_buffer.back() == '\n') if (flusher_data.msg_buffer.back() == '\n')
flusher_data.msg_buffer.pop_back(); flusher_data.msg_buffer.pop_back();
LOG_INFO(Debug_Emulated, "\n{}", flusher_data.msg_buffer);
LOG_CRITICAL(Debug_Emulated, "\n{}", flusher_data.msg_buffer);
flusher_data.msg_buffer.clear(); flusher_data.msg_buffer.clear();
} }
if (stop_token.stop_requested()) break; if (stop_token.stop_requested()) break;

7
src/core/hle/service/jit/jit_context.cpp

@ -60,13 +60,14 @@ public:
{} {}
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override { std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override {
static_assert(Core::Memory::YUZU_PAGESIZE == Dynarmic::CODE_PAGE_SIZE);
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
// todo: does code page have to be 4kib?
//static_assert(Core::Memory::YUZU_PAGESIZE == Dynarmic::CODE_PAGE_SIZE);
auto const aligned_vaddr = vaddr & ~(Dynarmic::CODE_PAGE_SIZE - 1);
if (last_code_addr != aligned_vaddr) { if (last_code_addr != aligned_vaddr) {
cached_code_page = ReadMemory<Dynarmic::CodePage>(aligned_vaddr); cached_code_page = ReadMemory<Dynarmic::CodePage>(aligned_vaddr);
last_code_addr = aligned_vaddr; last_code_addr = aligned_vaddr;
} }
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
return cached_code_page.inst[(vaddr & (Dynarmic::CODE_PAGE_SIZE - 1)) / sizeof(u32)];
} }
void InstructionSynchronizationBarrierRaised() override { void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch last_code_addr = u64(-1); //reset back, force refetch

2
src/core/loader/deconstructed_rom_directory.cpp

@ -232,7 +232,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
// TODO: this is bad form of ASLR, it sucks // TODO: this is bad form of ASLR, it sucks
std::uintptr_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue() std::uintptr_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xfff000;
? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xffffff & ~(Common::HostPageSize - 1);
// Setup the process code layout // Setup the process code layout
if (process.LoadFromMetadata(system.Kernel(), metadata, code_size, fastmem_base, aslr_offset).IsError()) { if (process.LoadFromMetadata(system.Kernel(), metadata, code_size, fastmem_base, aslr_offset).IsError()) {

2
src/core/loader/kip.cpp

@ -90,7 +90,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
// TODO: this is bad form of ASLR, it sucks // TODO: this is bad form of ASLR, it sucks
std::uintptr_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue() std::uintptr_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xfff000;
? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xffffff & ~(Common::HostPageSize - 1);
// Setup the process code layout // Setup the process code layout
if (process.LoadFromMetadata(system.Kernel(), FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset).IsError()) { if (process.LoadFromMetadata(system.Kernel(), FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset).IsError()) {

2
src/core/loader/nro.cpp

@ -276,7 +276,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
// TODO: this is bad form of ASLR, it sucks // TODO: this is bad form of ASLR, it sucks
std::uintptr_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue() std::uintptr_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xfff000;
? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xffffff & ~(Common::HostPageSize - 1);
// Setup the process code layout // Setup the process code layout
if (process if (process

Loading…
Cancel
Save