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} "")
endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX))
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX OR APPLE))
set(HAS_NCE 1)
add_compile_definitions(HAS_NCE=1)
endif()

4
externals/cmake-modules/DetectArchitecture.cmake

@ -40,8 +40,8 @@ if (CMAKE_OSX_ARCHITECTURES)
set(ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
# 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)
endforeach()

4
src/common/assert.cpp

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

4
src/common/assert.h

@ -17,8 +17,8 @@ void AssertFailSoftImpl();
[[noreturn]] void AssertFatalImpl();
// 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
#define YUZU_NO_INLINE __attribute__((noinline))
#endif

115
src/common/host_memory.cpp

@ -26,10 +26,8 @@
#if defined(__linux__)
#include <sys/random.h>
#elif defined(__APPLE__)
#include <sys/types.h>
#include <sys/random.h>
#include <mach/vm_map.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>
#elif defined(__FreeBSD__)
#include <sys/shm.h>
#elif defined(__OPENORBIS__)
@ -59,6 +57,7 @@
#include "common/free_region_manager.h"
#include "common/host_memory.h"
#include "common/logging.h"
#include "common/settings.h"
#if defined(__ANDROID__) && __ANDROID_API__ < 30
#include <sys/syscall.h>
@ -72,9 +71,6 @@ static int memfd_create(const char* name, unsigned int flags) {
namespace Common {
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
#ifdef _WIN32
// Manually imported for MinGW compatibility
@ -401,6 +397,7 @@ private:
#ifdef ARCHITECTURE_arm64
#ifndef __APPLE__
static void* ChooseVirtualBase(size_t virtual_size) {
constexpr uintptr_t Map39BitSize = (1ULL << 39);
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;
// Try to map.
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
void* map_pointer =
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 (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
@ -441,6 +437,56 @@ static void* ChooseVirtualBase(size_t virtual_size) {
#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) {
#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);
@ -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);
}
#endif
#endif // ARCHITECTURE_arm64
#if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__)
/// Most Unices don't have a portable shm_open (AIX, OpenBSD, NetBSD, Solaris 11, OpenIndiana)
@ -507,8 +553,6 @@ public:
{}
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
#if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__)
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));
return false;
}
// Virtual memory initialization
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
if (virtual_base == MAP_FAILED) {
@ -697,12 +740,13 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
#else
// Try to allocate a fastmem arena.
// 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()) {
backing_base = impl->backing_base;
virtual_base = impl->virtual_base;
if (virtual_base) {
// 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_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) {
#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(host_offset + length <= backing_size);
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) {
#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);
if (length == 0 || !virtual_base || !impl) {
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) {
#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);
if (length == 0 || !virtual_base || !impl) {
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);
#endif
}

13
src/common/host_memory.h

@ -8,12 +8,25 @@
#include <memory>
#include <optional>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/virtual_buffer.h"
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 {
Read = 1 << 0,
Write = 1 << 1,

8
src/common/settings.cpp

@ -177,15 +177,11 @@ bool IsDMALevelSafe() {
}
bool IsFastmemEnabled() {
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
if (values.cpu_accuracy.GetValue() == CpuAccuracy::Debugging)
return bool(values.cpuopt_fastmem);
else if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe)
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;
#else
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) {
if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32)))
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) {
m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page));
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) {

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) {
if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32)))
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) {
m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page));
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) {

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

@ -16,9 +16,7 @@
#include "core/hle/kernel/k_process.h"
#include <signal.h>
#include <sys/syscall.h>
#include <unistd.h>
namespace Core {
@ -33,14 +31,6 @@ static_assert(offsetof(NativeExecutionParameters, native_context) == TpidrEl0Nat
static_assert(offsetof(NativeExecutionParameters, lock) == TpidrEl0Lock);
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;
constexpr u32 StackSize = 128_KiB;
@ -48,32 +38,29 @@ constexpr u32 StackSize = 128_KiB;
void* ArmNce::RestoreGuestContext(void* raw_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.
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);
// Retrieve the host floating point state.
auto* fpctx = GetFloatingPointState(host_ctx);
// 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));
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));
// 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.
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 tpidr;
@ -81,47 +68,44 @@ void* ArmNce::RestoreGuestContext(void* raw_context) {
void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_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.
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.
host_ctx.sp = guest_ctx->host_ctx.host_sp;
*host_ctx.sp() = guest_ctx->host_ctx.host_sp;
// 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));
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));
// 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.
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) {
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);
// 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.
// This will allow games to continue in many scenarios where they would otherwise crash.
if (!is_prefetch_abort) {
host_ctx.pc += 4;
*host_ctx.pc() += 4;
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) {
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();
// Match and execute an instruction.
auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx, fpctx);
auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx);
if (next_pc) {
host_ctx.pc = *next_pc;
*host_ctx.pc() = *next_pc;
return true;
}
@ -278,7 +261,11 @@ ArmNce::~ArmNce() = default;
void ArmNce::Initialize() {
if (m_thread_id == -1) {
#if defined(__linux__)
m_thread_id = gettid();
#else
m_thread_id = pthread_mach_thread_np(pthread_self());
#endif
}
// Configure signal stack.
@ -381,14 +368,24 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
if (params->is_running) {
// We should signal to the running thread.
// The running thread will unlock the thread context.
#if defined(__linux__)
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 {
// If the thread is no longer running, we have nothing to do.
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() {
#ifdef __aarch64__

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

@ -7,12 +7,35 @@
mov reg, #(((val) >> 0x00) & 0xFFFF); \
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) */
.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 tpidr_el0 to x4. */
mov x3, sp
@ -50,10 +73,7 @@ _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm:
/* 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. */
/* On entry, x0 = thread id, which is already in the right place. */
@ -61,27 +81,30 @@ _ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv:
mov x9, x1
/* Set up arguments. */
mov x8, #(__NR_tkill)
/* On entry, x0 = thread id, which is already in the right place. */
mov x1, #(ReturnToRunCodeByExceptionLevelChangeSignal)
/* Tail call the signal handler. */
#ifndef __APPLE__
mov x8, #(__NR_tkill)
svc #0
#else
mov x16, #328
svc #0x80
#endif
/* Block execution from flowing here. */
brk #1000
/* 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]!
mov x29, sp
/* Call the context restorer with the raw context. */
mov x0, x2
bl _ZN4Core6ArmNce19RestoreGuestContextEPv
bl func(_ZN4Core6ArmNce19RestoreGuestContextEPv)
/* Save the old value of tpidr_el0. */
mrs x8, tpidr_el0
@ -92,7 +115,7 @@ _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
msr tpidr_el0, x0
/* Unlock the context. */
bl _ZN4Core6ArmNce22UnlockThreadParametersEPv
bl func(_ZN4Core6ArmNce22UnlockThreadParametersEPv)
/* Returning from here will enter the guest. */
ldp x29, x30, [sp], #0x10
@ -100,10 +123,7 @@ _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
/* 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. */
mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)]
@ -121,7 +141,7 @@ _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
/* Tail call the restorer. */
mov x1, x2
b _ZN4Core6ArmNce16SaveGuestContextEPNS_12GuestContextEPv
b func(_ZN4Core6ArmNce16SaveGuestContextEPNS_12GuestContextEPv)
/* Returning from here will enter host code. */
@ -131,10 +151,7 @@ _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
/* 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. */
mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)]
@ -146,7 +163,7 @@ _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
/* Incorrect TLS magic, so this is a host fault. */
/* Tail call the handler. */
b _ZN4Core6ArmNce24HandleHostAlignmentFaultEiPvS1_
b func(_ZN4Core6ArmNce24HandleHostAlignmentFaultEiPvS1_)
1:
/* Correct TLS magic, so this is a guest fault. */
@ -163,7 +180,7 @@ _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
msr tpidr_el0, x3
/* Call the handler. */
bl _ZN4Core6ArmNce25HandleGuestAlignmentFaultEPNS_12GuestContextEPvS3_
bl func(_ZN4Core6ArmNce25HandleGuestAlignmentFaultEPNS_12GuestContextEPvS3_)
/* If the handler returned false, we want to preserve the host tpidr_el0. */
cbz x0, 2f
@ -177,10 +194,7 @@ _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
ret
/* 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. */
mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)]
@ -192,7 +206,7 @@ _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
/* Incorrect TLS magic, so this is a host fault. */
/* Tail call the handler. */
b _ZN4Core6ArmNce21HandleHostAccessFaultEiPvS1_
b func(_ZN4Core6ArmNce21HandleHostAccessFaultEiPvS1_)
1:
/* Correct TLS magic, so this is a guest fault. */
@ -209,7 +223,7 @@ _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
msr tpidr_el0, x3
/* Call the handler. */
bl _ZN4Core6ArmNce22HandleGuestAccessFaultEPNS_12GuestContextEPvS3_
bl func(_ZN4Core6ArmNce22HandleGuestAccessFaultEPNS_12GuestContextEPvS3_)
/* If the handler returned false, we want to preserve the host tpidr_el0. */
cbz x0, 2f
@ -224,10 +238,7 @@ _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
/* 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. */
add x0, x0, #(TpidrEl0Lock)
@ -252,10 +263,7 @@ _ZN4Core6ArmNce20LockThreadParametersEPv:
/* 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. */
add x0, x0, #(TpidrEl0Lock)

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

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

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

@ -10,6 +10,9 @@
#include "core/arm/arm_interface.h"
#include "core/arm/nce/arm_nce_asm_definitions.h"
#include <unistd.h>
#include <signal.h>
namespace Core {
class ArmNce;
@ -40,6 +43,90 @@ struct GuestContext {
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.
static_assert(offsetof(GuestContext, sp) == GuestContextSp);
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_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

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

@ -8,6 +8,8 @@
#include <numeric>
#include "core/arm/nce/interpreter_visitor.h"
#include "guest_context.h"
namespace Core {
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);
}
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);
u32 instruction = memory.Read32(pc);

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

@ -18,8 +18,9 @@
#include "core/arm/nce/visitor_base.h"
namespace Core {
class KernelContext;
namespace Memory {
namespace Memory {
class Memory;
}
@ -105,7 +106,6 @@ private:
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

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

@ -10,6 +10,8 @@
#include "core/arm/nce/guest_context.h"
#include "core/arm/nce/instructions.h"
#include "core/arm/nce/patcher.h"
#include "common/host_memory.h"
#include "core/core.h"
#include "core/core_timing.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 {
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 {
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) {

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

@ -212,6 +212,11 @@ KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, siz
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.
const auto [pool, dir] = DecodeOption(option);
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')
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();
}
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 {
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) {
cached_code_page = ReadMemory<Dynarmic::CodePage>(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 {
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
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
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
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
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
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
if (process

Loading…
Cancel
Save