
This commit adds all the changes made to the 3GX plugin loader fork of Luma3DS. The most important features are: - Add 3GX plugin loader support. New service added to rosalina: plg:ldr - Add svcControlProcess, svcControlMemoryUnsafe and improve svcMapProcessMemoryEx (breaking change) - Allow applications to override certain configurations depending on their needs: - Disable core2 thread redirection - Disable game patching for the next app - Force New 3DS speedup - Force next application in a specific memory mode - Block the opening of the Rosalina menu - Add GDB commands to list all process handles and catch all SVC (latter is for IDA Pro as gdb client supports it) - Other changes necessary for plugins to work properly. Please check changed files in this PR for more details. --------- Co-authored-by: PabloMK7 <hackyglitch@gmail.com> Co-authored-by: Nanquitas <nath.doidi@gmail.com> Co-authored-by: TuxSH <1922548+TuxSH@users.noreply.github.com>
340 lines
7.7 KiB
C
340 lines
7.7 KiB
C
#include <3ds.h>
|
|
#include "plugin.h"
|
|
#include <string.h>
|
|
#include "csvc.h"
|
|
|
|
static Handle plgLdrHandle;
|
|
static Handle plgLdrArbiter;
|
|
static s32* plgEvent;
|
|
static s32* plgReply;
|
|
static int plgLdrRefCount;
|
|
static bool plgIsN3DS;
|
|
static OnPlgLdrEventCb_t onPlgLdrEventCb = NULL;
|
|
|
|
static Result PLGLDR__GetArbiter(void)
|
|
{
|
|
Result res = 0;
|
|
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(9, 0, 0);
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
res = cmdbuf[1];
|
|
plgLdrArbiter = cmdbuf[3];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Result plgLdrInit(void)
|
|
{
|
|
s64 out = 0;
|
|
Result res = 0;
|
|
|
|
svcGetSystemInfo(&out, 0x10000, 0x201);
|
|
plgIsN3DS = out != 0;
|
|
if (AtomicPostIncrement(&plgLdrRefCount) == 0)
|
|
res = svcConnectToPort(&plgLdrHandle, "plg:ldr");
|
|
|
|
if (R_SUCCEEDED(res)
|
|
&& R_SUCCEEDED((res = PLGLDR__GetArbiter())))
|
|
{
|
|
PluginHeader *header = (PluginHeader *)0x07000000;
|
|
|
|
plgEvent = header->plgldrEvent;
|
|
plgReply = header->plgldrReply;
|
|
}
|
|
else
|
|
plgLdrExit();
|
|
|
|
return res;
|
|
}
|
|
|
|
void plgLdrExit(void)
|
|
{
|
|
if (AtomicDecrement(&plgLdrRefCount))
|
|
return;
|
|
|
|
if (plgLdrHandle)
|
|
svcCloseHandle(plgLdrHandle);
|
|
if (plgLdrArbiter)
|
|
svcCloseHandle(plgLdrArbiter);
|
|
plgLdrHandle = plgLdrArbiter = 0;
|
|
}
|
|
|
|
Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled)
|
|
{
|
|
Result res = 0;
|
|
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(2, 0, 0);
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
res = cmdbuf[1];
|
|
*isEnabled = cmdbuf[2];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Result PLGLDR__SetPluginLoaderState(bool enabled)
|
|
{
|
|
Result res = 0;
|
|
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(3, 1, 0);
|
|
cmdbuf[1] = (u32)enabled;
|
|
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
res = cmdbuf[1];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters)
|
|
{
|
|
Result res = 0;
|
|
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(4, 2, 4);
|
|
cmdbuf[1] = (u32)parameters->noFlash | (((u32)parameters->pluginMemoryStrategy) << 8);
|
|
cmdbuf[2] = parameters->lowTitleId;
|
|
cmdbuf[3] = IPC_Desc_Buffer(256, IPC_BUFFER_R);
|
|
cmdbuf[4] = (u32)parameters->path;
|
|
cmdbuf[5] = IPC_Desc_Buffer(32 * sizeof(u32), IPC_BUFFER_R);
|
|
cmdbuf[6] = (u32)parameters->config;
|
|
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
res = cmdbuf[1];
|
|
}
|
|
return res;
|
|
}
|
|
void Flash(u32 color);
|
|
Result PLGLDR__DisplayMenu(PluginMenu *menu)
|
|
{
|
|
Result res = 0;
|
|
|
|
u32 nbItems = menu->nbItems;
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(5, 1, 8);
|
|
cmdbuf[1] = nbItems;
|
|
cmdbuf[2] = IPC_Desc_Buffer(nbItems, IPC_BUFFER_RW);
|
|
cmdbuf[3] = (u32)menu->states;
|
|
cmdbuf[4] = IPC_Desc_Buffer(MAX_BUFFER, IPC_BUFFER_R);
|
|
cmdbuf[5] = (u32)menu->title;
|
|
cmdbuf[6] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R);
|
|
cmdbuf[7] = (u32)menu->items;
|
|
cmdbuf[8] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R);
|
|
cmdbuf[9] = (u32)menu->hints;
|
|
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
res = cmdbuf[1];
|
|
}
|
|
else Flash(0xFF0000);
|
|
|
|
return res;
|
|
}
|
|
|
|
Result PLGLDR__DisplayMessage(const char *title, const char *body)
|
|
{
|
|
Result res = 0;
|
|
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(6, 0, 4);
|
|
cmdbuf[1] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R);
|
|
cmdbuf[2] = (u32)title;
|
|
cmdbuf[3] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R);
|
|
cmdbuf[4] = (u32)body;
|
|
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
res = cmdbuf[1];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Result PLGLDR__DisplayErrMessage(const char *title, const char *body, u32 error)
|
|
{
|
|
Result res = 0;
|
|
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(7, 1, 4);
|
|
cmdbuf[1] = error;
|
|
cmdbuf[2] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R);
|
|
cmdbuf[3] = (u32)title;
|
|
cmdbuf[4] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R);
|
|
cmdbuf[5] = (u32)body;
|
|
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
res = cmdbuf[1];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Result PLGLDR__GetVersion(u32 *version)
|
|
{
|
|
Result res = 0;
|
|
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(8, 0, 0);
|
|
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
if (cmdbuf[0] != IPC_MakeHeader(8, 2, 0))
|
|
return 0xD900182F;
|
|
|
|
res = cmdbuf[1];
|
|
if (version)
|
|
*version = cmdbuf[2];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Result PLGLDR__GetPluginPath(char *path)
|
|
{
|
|
if (path == NULL)
|
|
return MAKERESULT(28, 7, 254, 1014); ///< Usage, App, Invalid argument
|
|
|
|
Result res = 0;
|
|
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(10, 0, 2);
|
|
cmdbuf[1] = IPC_Desc_Buffer(255, IPC_BUFFER_RW);
|
|
cmdbuf[2] = (u32)path;
|
|
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
res = cmdbuf[1];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Result PLGLDR__SetRosalinaMenuBlock(bool shouldBlock)
|
|
{
|
|
Result res = 0;
|
|
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(11, 1, 0);
|
|
cmdbuf[1] = (u32)shouldBlock;
|
|
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
res = cmdbuf[1];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Result PLGLDR__SetSwapSettings(char* swapPath, void* saveFunc, void* loadFunc, void* args)
|
|
{
|
|
Result res = 0;
|
|
|
|
const char* trueSwapPath;
|
|
if (swapPath) trueSwapPath = swapPath;
|
|
else trueSwapPath = "\0";
|
|
|
|
u32 buf[4] = { 0 };
|
|
u32* trueArgs;
|
|
if (args) trueArgs = args;
|
|
else trueArgs = buf;
|
|
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(12, 2, 4);
|
|
cmdbuf[1] = (saveFunc) ? svcConvertVAToPA(saveFunc, false) | (1 << 31) : 0;
|
|
cmdbuf[2] = (loadFunc) ? svcConvertVAToPA(loadFunc, false) | (1 << 31) : 0;
|
|
cmdbuf[3] = IPC_Desc_Buffer(4 * sizeof(u32), IPC_BUFFER_R);
|
|
cmdbuf[4] = (u32)trueArgs;
|
|
cmdbuf[5] = IPC_Desc_Buffer(strlen(trueSwapPath) + 1, IPC_BUFFER_R);
|
|
cmdbuf[6] = (u32)trueSwapPath;
|
|
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
res = cmdbuf[1];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Result PLGLDR__SetExeLoadSettings(void* loadFunc, void* args)
|
|
{
|
|
Result res = 0;
|
|
|
|
u32 buf[4] = { 0 };
|
|
u32* trueArgs;
|
|
if (args) trueArgs = args;
|
|
else trueArgs = buf;
|
|
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(13, 1, 2);
|
|
cmdbuf[1] = (loadFunc) ? svcConvertVAToPA(loadFunc, false) | (1 << 31) : 0;
|
|
cmdbuf[2] = IPC_Desc_Buffer(4 * sizeof(u32), IPC_BUFFER_R);
|
|
cmdbuf[3] = (u32)trueArgs;
|
|
|
|
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
|
{
|
|
res = cmdbuf[1];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
void PLGLDR__SetEventCallback(OnPlgLdrEventCb_t cb)
|
|
{
|
|
onPlgLdrEventCb = cb;
|
|
}
|
|
|
|
static s32 __ldrex__(s32 *addr)
|
|
{
|
|
s32 val;
|
|
do
|
|
val = __ldrex(addr);
|
|
while (__strex(addr, val));
|
|
|
|
return val;
|
|
}
|
|
|
|
static void __strex__(s32 *addr, s32 val)
|
|
{
|
|
do
|
|
__ldrex(addr);
|
|
while (__strex(addr, val));
|
|
}
|
|
|
|
void PLGLDR__Status(void)
|
|
{
|
|
s32 event = __ldrex__(plgEvent); // exclusive read necessary?
|
|
|
|
if (event <= 0)
|
|
return;
|
|
|
|
if (onPlgLdrEventCb)
|
|
onPlgLdrEventCb(event);
|
|
|
|
__strex__(plgReply, PLG_OK);
|
|
__strex__(plgEvent, PLG_WAIT);
|
|
|
|
if (event < PLG_ABOUT_TO_SWAP)
|
|
return;
|
|
|
|
svcArbitrateAddress(plgLdrArbiter, (u32)plgReply, ARBITRATION_SIGNAL, 1, 0);
|
|
if (event == PLG_ABOUT_TO_SWAP)
|
|
svcArbitrateAddress(plgLdrArbiter, (u32)plgEvent, ARBITRATION_WAIT_IF_LESS_THAN, PLG_OK, 0);
|
|
else if (event == PLG_ABOUT_TO_EXIT)
|
|
{
|
|
plgLdrExit();
|
|
svcExitThread();
|
|
}
|
|
}
|