
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>
209 lines
7.4 KiB
C
209 lines
7.4 KiB
C
#include "svc/ControlProcess.h"
|
|
#include "memory.h"
|
|
#include "mmu.h"
|
|
#include "synchronization.h"
|
|
|
|
typedef bool (*ThreadPredicate)(KThread *thread);
|
|
|
|
// Lock bit has to be different from Rosalina to avoid unintended unlock when using Rosalina menu
|
|
static void rescheduleThread(KThread *thread, bool lock)
|
|
{
|
|
KRecursiveLock__Lock(criticalSectionLock);
|
|
|
|
u32 oldSchedulingMask = thread->schedulingMask;
|
|
if(lock)
|
|
thread->schedulingMask |= 0x20;
|
|
else
|
|
thread->schedulingMask &= ~0x20;
|
|
|
|
KScheduler__AdjustThread(currentCoreContext->objectContext.currentScheduler, thread, oldSchedulingMask);
|
|
|
|
KRecursiveLock__Unlock(criticalSectionLock);
|
|
}
|
|
|
|
static void lockThread(KThread *thread)
|
|
{
|
|
KThread *syncThread = synchronizationMutex->owner;
|
|
|
|
if(syncThread == NULL || syncThread != thread)
|
|
rescheduleThread(thread, true);
|
|
}
|
|
|
|
Result ControlProcess(Handle processHandle, ProcessOp op, u32 varg2, u32 varg3)
|
|
{
|
|
Result res = 0;
|
|
KProcess *process;
|
|
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
|
|
|
|
if(processHandle == CUR_PROCESS_HANDLE)
|
|
{
|
|
process = currentCoreContext->objectContext.currentProcess;
|
|
KAutoObject__AddReference((KAutoObject *)process);
|
|
}
|
|
else
|
|
process = KProcessHandleTable__ToKProcess(handleTable, processHandle);
|
|
|
|
if(process == NULL)
|
|
return 0xD8E007F7; // invalid handle
|
|
|
|
switch (op)
|
|
{
|
|
case PROCESSOP_GET_ALL_HANDLES:
|
|
{
|
|
KProcessHandleTable *table = handleTableOfProcess(process);
|
|
u32 *originalHandleList = (u32 *)varg2;
|
|
u32 count = 0;
|
|
u32 searchForToken = varg3;
|
|
HandleDescriptor *handleDesc = table->handleTable == NULL ? table->internalTable : table->handleTable;
|
|
|
|
for (u32 idx = 0; idx < (u32)table->maxHandleCount; ++idx, ++handleDesc)
|
|
{
|
|
if (handleDesc->pointer == NULL)
|
|
continue;
|
|
|
|
if (searchForToken)
|
|
{
|
|
KClassToken token;
|
|
|
|
handleDesc->pointer->vtable->GetClassToken(&token, handleDesc->pointer);
|
|
if (searchForToken != token.flags)
|
|
continue;
|
|
}
|
|
|
|
*originalHandleList++ = idx | ((handleDesc->info << 16) >> 1);
|
|
++count;
|
|
}
|
|
res = count;
|
|
break;
|
|
}
|
|
|
|
case PROCESSOP_SET_MMU_TO_RWX:
|
|
{
|
|
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
|
|
|
|
*KPROCESS_GET_PTR(process, customFlags) |= ForceRWXPages;
|
|
KProcessHwInfo__SetMMUTableToRWX(hwInfo);
|
|
break;
|
|
}
|
|
case PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT:
|
|
{
|
|
// Only accept current process for this command
|
|
if (process != currentCoreContext->objectContext.currentProcess)
|
|
{
|
|
res = 0xD8E007F7; // invalid handle
|
|
break;
|
|
}
|
|
|
|
Handle *onMemoryLayoutChangeEvent = KPROCESS_GET_PTR(process, onMemoryLayoutChangeEvent);
|
|
|
|
if (*onMemoryLayoutChangeEvent == 0)
|
|
res = CreateEvent(onMemoryLayoutChangeEvent, RESET_ONESHOT);
|
|
|
|
if (res >= 0)
|
|
{
|
|
*KPROCESS_GET_PTR(process, customFlags) |= SignalOnMemLayoutChanges;
|
|
KAutoObject * event = KProcessHandleTable__ToKAutoObject(handleTable, *onMemoryLayoutChangeEvent);
|
|
|
|
createHandleForThisProcess((Handle *)varg2, event);
|
|
((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case PROCESSOP_SIGNAL_ON_EXIT:
|
|
{
|
|
*KPROCESS_GET_PTR(process, customFlags) |= SignalOnExit;
|
|
break;
|
|
}
|
|
case PROCESSOP_GET_PA_FROM_VA:
|
|
{
|
|
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
|
|
|
|
u32 pa = KProcessHwInfo__GetPAFromVA(hwInfo, varg3);
|
|
*(u32 *)varg2 = pa;
|
|
|
|
if (pa == 0)
|
|
res = 0xE0E01BF5; ///< Invalid address
|
|
|
|
break;
|
|
}
|
|
case PROCESSOP_SCHEDULE_THREADS:
|
|
{
|
|
ThreadPredicate threadPredicate = (ThreadPredicate)varg3;
|
|
|
|
KRecursiveLock__Lock(criticalSectionLock);
|
|
|
|
if (varg2 == 0) // Unlock
|
|
{
|
|
for (KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
|
{
|
|
KThread *thread = (KThread *)node->key;
|
|
|
|
if ((thread->schedulingMask & 0xF) == 2) // thread is terminating
|
|
continue;
|
|
|
|
if (thread->ownerProcess == process && (thread->schedulingMask & 0x20)
|
|
&& (threadPredicate == NULL || threadPredicate(thread)))
|
|
rescheduleThread(thread, false);
|
|
}
|
|
}
|
|
else // Lock
|
|
{
|
|
bool currentThreadsFound = false;
|
|
|
|
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
|
{
|
|
KThread *thread = (KThread *)node->key;
|
|
|
|
if(thread->ownerProcess != process
|
|
|| (threadPredicate != NULL && !threadPredicate(thread)))
|
|
continue;
|
|
|
|
if(thread == coreCtxs[thread->coreId].objectContext.currentThread)
|
|
currentThreadsFound = true;
|
|
else
|
|
lockThread(thread);
|
|
}
|
|
|
|
if(currentThreadsFound)
|
|
{
|
|
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
|
{
|
|
KThread *thread = (KThread *)node->key;
|
|
|
|
if(thread->ownerProcess != process
|
|
|| (threadPredicate != NULL && !threadPredicate(thread)))
|
|
continue;
|
|
|
|
if(!(thread->schedulingMask & 0x20))
|
|
{
|
|
lockThread(thread);
|
|
KRecursiveLock__Lock(criticalSectionLock);
|
|
if(thread->coreId != getCurrentCoreID())
|
|
{
|
|
u32 cpsr = __get_cpsr();
|
|
__disable_irq();
|
|
coreCtxs[thread->coreId].objectContext.currentScheduler->triggerCrossCoreInterrupt = true;
|
|
currentCoreContext->objectContext.currentScheduler->triggerCrossCoreInterrupt = true;
|
|
__set_cpsr_cx(cpsr);
|
|
}
|
|
KRecursiveLock__Unlock(criticalSectionLock);
|
|
}
|
|
}
|
|
KScheduler__TriggerCrossCoreInterrupt(currentCoreContext->objectContext.currentScheduler);
|
|
}
|
|
}
|
|
|
|
KRecursiveLock__Unlock(criticalSectionLock);
|
|
break;
|
|
}
|
|
default:
|
|
res = 0xF8C007F4;
|
|
}
|
|
|
|
((KAutoObject *)process)->vtable->DecrementReferenceCount((KAutoObject *)process);
|
|
|
|
return res;
|
|
}
|