Better process implementation (#491)

* Initial implementation of KProcess

* Some improvements to the memory manager, implement back guest stack trace printing

* Better GetInfo implementation, improve checking in some places with information from process capabilities

* Allow the cpu to read/write from the correct memory locations for accesses crossing a page boundary

* Change long -> ulong for address/size on memory related methods to avoid unnecessary casts

* Attempt at implementing ldr:ro with new KProcess

* Allow BSS with size 0 on ldr:ro

* Add checking for memory block slab heap usage, return errors if full, exit gracefully

* Use KMemoryBlockSize const from KMemoryManager

* Allow all methods to read from non-contiguous locations

* Fix for TransactParcelAuto

* Address PR feedback, additionally fix some small issues related to the KIP loader and implement SVCs GetProcessId, GetProcessList, GetSystemInfo, CreatePort and ManageNamedPort

* Fix wrong check for source pages count from page list on MapPhysicalMemory

* Fix some issues with UnloadNro on ldr:ro
This commit is contained in:
gdkchan 2018-11-28 20:18:09 -02:00 committed by GitHub
parent e7fe7d7247
commit 00579927e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
119 changed files with 7998 additions and 3232 deletions

View file

@ -7,48 +7,84 @@ namespace Ryujinx.HLE.HOS.Kernel
{
partial class SvcHandler
{
private void SvcCreateThread(CpuThreadState ThreadState)
private void CreateThread64(CpuThreadState ThreadState)
{
long EntryPoint = (long)ThreadState.X1;
long ArgsPtr = (long)ThreadState.X2;
long StackTop = (long)ThreadState.X3;
int Priority = (int)ThreadState.X4;
int ProcessorId = (int)ThreadState.X5;
ulong Entrypoint = ThreadState.X1;
ulong ArgsPtr = ThreadState.X2;
ulong StackTop = ThreadState.X3;
int Priority = (int)ThreadState.X4;
int CpuCore = (int)ThreadState.X5;
if ((uint)Priority > 0x3f)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!");
KernelResult Result = CreateThread(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, out int Handle);
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPriority);
return;
}
if (ProcessorId == -2)
{
//TODO: Get this value from the NPDM file.
ProcessorId = 0;
}
else if ((uint)ProcessorId > 3)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
return;
}
int Handle = Process.MakeThread(
EntryPoint,
StackTop,
ArgsPtr,
Priority,
ProcessorId);
ThreadState.X0 = 0;
ThreadState.X0 = (ulong)Result;
ThreadState.X1 = (ulong)Handle;
}
private KernelResult CreateThread(
ulong Entrypoint,
ulong ArgsPtr,
ulong StackTop,
int Priority,
int CpuCore,
out int Handle)
{
Handle = 0;
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if (CpuCore == -2)
{
CpuCore = CurrentProcess.DefaultCpuCore;
}
if ((uint)CpuCore >= KScheduler.CpuCoresCount || !CurrentProcess.IsCpuCoreAllowed(CpuCore))
{
return KernelResult.InvalidCpuCore;
}
if ((uint)Priority >= KScheduler.PrioritiesCount || !CurrentProcess.IsPriorityAllowed(Priority))
{
return KernelResult.InvalidPriority;
}
long Timeout = KTimeManager.ConvertMillisecondsToNanoseconds(100);
if (CurrentProcess.ResourceLimit != null &&
!CurrentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1, Timeout))
{
return KernelResult.ResLimitExceeded;
}
KThread Thread = new KThread(System);
KernelResult Result = CurrentProcess.InitializeThread(
Thread,
Entrypoint,
ArgsPtr,
StackTop,
Priority,
CpuCore);
if (Result != KernelResult.Success)
{
CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
return Result;
}
Result = Process.HandleTable.GenerateHandle(Thread, out Handle);
if (Result != KernelResult.Success)
{
Thread.Terminate();
CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
}
return Result;
}
private void SvcStartThread(CpuThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
@ -57,11 +93,11 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Thread != null)
{
long Result = Thread.Start();
KernelResult Result = Thread.Start();
if (Result != 0)
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
ThreadState.X0 = (ulong)Result;
@ -78,9 +114,9 @@ namespace Ryujinx.HLE.HOS.Kernel
{
KThread CurrentThread = System.Scheduler.GetCurrentThread();
CurrentThread.Exit();
System.Scheduler.ExitThread(CurrentThread);
System.Scheduler.StopThread(CurrentThread);
CurrentThread.Exit();
}
private void SvcSleepThread(CpuThreadState ThreadState)
@ -176,46 +212,60 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
private void SvcSetThreadCoreMask(CpuThreadState ThreadState)
private void SetThreadCoreMask64(CpuThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
int PrefferedCore = (int)ThreadState.X1;
int PreferredCore = (int)ThreadState.X1;
long AffinityMask = (long)ThreadState.X2;
Logger.PrintDebug(LogClass.KernelSvc,
"Handle = 0x" + Handle .ToString("x8") + ", " +
"PrefferedCore = 0x" + PrefferedCore.ToString("x8") + ", " +
"PreferredCore = 0x" + PreferredCore.ToString("x8") + ", " +
"AffinityMask = 0x" + AffinityMask .ToString("x16"));
if (PrefferedCore == -2)
{
//TODO: Get this value from the NPDM file.
PrefferedCore = 0;
KernelResult Result = SetThreadCoreMask(Handle, PreferredCore, AffinityMask);
AffinityMask = 1 << PrefferedCore;
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
ThreadState.X0 = (ulong)Result;
}
private KernelResult SetThreadCoreMask(int Handle, int PreferredCore, long AffinityMask)
{
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if (PreferredCore == -2)
{
PreferredCore = CurrentProcess.DefaultCpuCore;
AffinityMask = 1 << PreferredCore;
}
else
{
//TODO: Check allowed cores from NPDM file.
if ((uint)PrefferedCore > 3)
if ((CurrentProcess.Capabilities.AllowedCpuCoresMask | AffinityMask) !=
CurrentProcess.Capabilities.AllowedCpuCoresMask)
{
if ((PrefferedCore | 2) != -1)
return KernelResult.InvalidCpuCore;
}
if (AffinityMask == 0)
{
return KernelResult.InvalidCombination;
}
if ((uint)PreferredCore > 3)
{
if ((PreferredCore | 2) != -1)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
return;
return KernelResult.InvalidCpuCore;
}
}
else if ((AffinityMask & (1 << PrefferedCore)) == 0)
else if ((AffinityMask & (1 << PreferredCore)) == 0)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{AffinityMask:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
return;
return KernelResult.InvalidCombination;
}
}
@ -223,26 +273,15 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Thread == null)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
return KernelResult.InvalidHandle;
}
long Result = Thread.SetCoreAndAffinityMask(PrefferedCore, AffinityMask);
if (Result != 0)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
ThreadState.X0 = (ulong)Result;
return Thread.SetCoreAndAffinityMask(PreferredCore, AffinityMask);
}
private void SvcGetCurrentProcessorNumber(CpuThreadState ThreadState)
{
ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).CurrentCore;
ThreadState.X0 = (ulong)System.Scheduler.GetCurrentThread().CurrentCore;
}
private void SvcGetThreadId(CpuThreadState ThreadState)
@ -254,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Thread != null)
{
ThreadState.X0 = 0;
ThreadState.X1 = (ulong)Thread.ThreadId;
ThreadState.X1 = (ulong)Thread.ThreadUid;
}
else
{
@ -280,15 +319,24 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if (Thread.Owner != Process)
if (Thread.Owner != System.Scheduler.GetCurrentProcess())
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread owner process!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
}
if (Thread == System.Scheduler.GetCurrentThread())
{
Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
ThreadState.X0 = (ulong)KernelResult.InvalidThread;
return;
}
long Result = Thread.SetActivity(Pause);
if (Result != 0)
@ -304,6 +352,9 @@ namespace Ryujinx.HLE.HOS.Kernel
long Position = (long)ThreadState.X0;
int Handle = (int)ThreadState.X1;
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
KThread CurrentThread = System.Scheduler.GetCurrentThread();
KThread Thread = Process.HandleTable.GetObject<KThread>(Handle);
if (Thread == null)
@ -315,9 +366,18 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if (Process.GetThread(ThreadState.Tpidr) == Thread)
if (Thread.Owner != CurrentProcess)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
}
if (CurrentThread == Thread)
{
Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread);