(3DS) add dynarec support when started from a .cia install

use libkhax for ninjhax 1.0
This commit is contained in:
aliaspider 2015-09-11 22:02:04 +01:00
parent a99c3552a4
commit c1d061e204
9 changed files with 1637 additions and 19 deletions

98
3ds/3ds_utils.c Normal file
View File

@ -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();
}

12
3ds/3ds_utils.h Normal file
View File

@ -0,0 +1,12 @@
#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);
#endif // _3DS_UTILS_H

22
3ds/libkhax/LICENSE Normal file
View File

@ -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.

16
3ds/libkhax/khax.h Normal file
View File

@ -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

1110
3ds/libkhax/khaxinit.cpp Normal file

File diff suppressed because it is too large Load Diff

339
3ds/libkhax/khaxinternal.h Normal file
View File

@ -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
}

View File

@ -209,7 +209,7 @@ else ifeq ($(platform), ctr)
CFLAGS += -Wall -mword-relocations
CFLAGS += -fomit-frame-pointer -ffast-math
CPU_ARCH := arm
# dynarec currently requires ninjahax to work
# dynarec unavailable on ninjhax 2.0
HAVE_DYNAREC = 1
STATIC_LINKING = 1
@ -322,6 +322,7 @@ ifeq ($(CPU_ARCH), arm)
DEFINES += -DARM_ARCH
endif
WARNINGS_DEFINES =
CODE_DEFINES =
@ -334,6 +335,23 @@ ifeq ($(FRONTEND_SUPPORTS_RGB565), 1)
endif
ifeq ($(platform), ctr)
ifeq ($(HAVE_DYNAREC), 1)
OBJECTS += 3ds/3ds_utils.o 3ds/libkhax/khaxinit.o
ifeq ($(strip $(CTRULIB)),)
$(error "Please set CTRULIB in your environment. export CTRULIB=<path to>ctrulib")
endif
CFLAGS += -I$(CTRULIB)/include
3ds/libkhax/khaxinit.o: 3ds/libkhax/khaxinit.cpp
$(CXX) $(INCFLAGS) $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 $(OPTIMIZE) -c -o $@ $<
endif
endif
ifeq ($(platform), theos_ios)
COMMON_FLAGS := -DIOS $(COMMON_DEFINES) $(INCFLAGS) -I$(THEOS_INCLUDE_PATH) -Wno-error
$(LIBRARY_NAME)_CFLAGS += $(COMMON_FLAGS) $(CFLAGS)

View File

@ -234,8 +234,8 @@ extern u8 bit_count[256];
#if defined(PSP_BUILD)
#define translate_invalidate_dcache() sceKernelDcacheWritebackAll()
#elif defined(_3DS)
int32_t HB_FlushInvalidateCache();
#define translate_invalidate_dcache() HB_FlushInvalidateCache()
#include "3ds/3ds_utils.h"
#define translate_invalidate_dcache() ctr_flush_invalidate_cache()
#define invalidate_icache_region(addr, size) (void)0
#elif defined(ARM_ARCH)
static int sys_cacheflush(void *addr, unsigned long size)

View File

@ -12,11 +12,11 @@
void* linearMemAlign(size_t size, size_t alignment);
void linearFree(void* mem);
#if defined(HAVE_DYNAREC)
int32_t hbInit(void);
void hbExit(void);
int32_t HB_FlushInvalidateCache(void);
int32_t HB_ReprotectMemory(void* addr, uint32_t pages, uint32_t mode, uint32_t* reprotectedPages);
int hb_service_available;
#include "3ds/3ds_utils.h"
#define MEMOP_PROT 6
int32_t svcDuplicateHandle(uint32_t* out, uint32_t original);
int32_t svcControlProcessMemory(uint32_t process, void* addr0, void* addr1, uint32_t size, uint32_t type, uint32_t perm);
int ctr_has_full_svc_access;
#endif
#endif
@ -117,16 +117,19 @@ void retro_init(void)
init_gamepak_buffer();
init_sound(1);
#if defined(_3DS) && defined(HAVE_DYNAREC)
hb_service_available = !hbInit();
if (hb_service_available)
ctr_has_full_svc_access = ctr_svchack_init();
if (ctr_has_full_svc_access)
{
HB_ReprotectMemory(rom_translation_cache,
ROM_TRANSLATION_CACHE_SIZE / 0x1000, 0b111, NULL);
HB_ReprotectMemory(ram_translation_cache,
RAM_TRANSLATION_CACHE_SIZE / 0x1000, 0b111, NULL);
HB_ReprotectMemory(bios_translation_cache,
BIOS_TRANSLATION_CACHE_SIZE / 0x1000, 0b111, NULL);
HB_FlushInvalidateCache();
uint32_t currentHandle;
svcDuplicateHandle(&currentHandle, 0xFFFF8001);
svcControlProcessMemory(currentHandle, rom_translation_cache, 0x0,
ROM_TRANSLATION_CACHE_SIZE, MEMOP_PROT, 0b111);
svcControlProcessMemory(currentHandle, ram_translation_cache, 0x0,
RAM_TRANSLATION_CACHE_SIZE, MEMOP_PROT, 0b111);
svcControlProcessMemory(currentHandle, bios_translation_cache, 0x0,
BIOS_TRANSLATION_CACHE_SIZE, MEMOP_PROT, 0b111);
ctr_flush_invalidate_cache();
}
#endif
@ -149,7 +152,7 @@ void retro_deinit(void)
munmap(bios_translation_cache, BIOS_TRANSLATION_CACHE_SIZE);
#endif
#if defined(_3DS) && defined(HAVE_DYNAREC)
hbExit();
ctr_svchack_exit();
#endif
#ifdef _3DS
linearFree(gba_screen_pixels);
@ -315,7 +318,7 @@ bool retro_load_game(const struct retro_game_info* info)
ram_translation_ptr = ram_translation_cache;
bios_translation_ptr = bios_translation_cache;
#elif defined(_3DS)
if(!hb_service_available)
if(!ctr_has_full_svc_access)
dynarec_enable = 0;
#endif
}