aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/KThread.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-11-28 20:18:09 -0200
committerGitHub <noreply@github.com>2018-11-28 20:18:09 -0200
commit00579927e43bf55ee06ae02933c1e755fb4120eb (patch)
tree0fd06db7b28e0accf87b465ec6f4dc74691febab /Ryujinx.HLE/HOS/Kernel/KThread.cs
parente7fe7d724778535f8eff390abef54274a343c0b7 (diff)
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
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/KThread.cs')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KThread.cs337
1 files changed, 242 insertions, 95 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/KThread.cs b/Ryujinx.HLE/HOS/Kernel/KThread.cs
index 73ee2322..88f144c8 100644
--- a/Ryujinx.HLE/HOS/Kernel/KThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KThread.cs
@@ -1,4 +1,5 @@
using ChocolArm64;
+using ChocolArm64.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -13,20 +14,30 @@ namespace Ryujinx.HLE.HOS.Kernel
public long AffinityMask { get; set; }
- public int ThreadId { get; private set; }
+ public long ThreadUid { get; private set; }
- public KSynchronizationObject SignaledObj;
+ public long TotalTimeRunning { get; set; }
+
+ public KSynchronizationObject SignaledObj { get; set; }
public long CondVarAddress { get; set; }
- public long MutexAddress { get; set; }
- public Process Owner { get; private set; }
+ private ulong Entrypoint;
+
+ public long MutexAddress { get; set; }
+
+ public KProcess Owner { get; private set; }
- public long LastScheduledTicks { get; set; }
+ private ulong TlsAddress;
+
+ public long LastScheduledTime { get; set; }
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
- private LinkedListNode<KThread> WithholderNode;
+ public LinkedList<KThread> Withholder { get; set; }
+ public LinkedListNode<KThread> WithholderNode { get; set; }
+
+ public LinkedListNode<KThread> ProcessListNode { get; set; }
private LinkedList<KThread> MutexWaiters;
private LinkedListNode<KThread> MutexWaiterNode;
@@ -65,38 +76,131 @@ namespace Ryujinx.HLE.HOS.Kernel
public long LastPc { get; set; }
- public KThread(
- CpuThread Thread,
- Process Process,
- Horizon System,
- int ProcessorId,
- int Priority,
- int ThreadId) : base(System)
+ public KThread(Horizon System) : base(System)
{
- this.ThreadId = ThreadId;
-
- Context = Thread;
- Owner = Process;
- PreferredCore = ProcessorId;
Scheduler = System.Scheduler;
SchedulingData = System.Scheduler.SchedulingData;
SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
MutexWaiters = new LinkedList<KThread>();
+ }
+
+ public KernelResult Initialize(
+ ulong Entrypoint,
+ ulong ArgsPtr,
+ ulong StackTop,
+ int Priority,
+ int DefaultCpuCore,
+ KProcess Owner,
+ ThreadType Type = ThreadType.User)
+ {
+ if ((uint)Type > 3)
+ {
+ throw new ArgumentException($"Invalid thread type \"{Type}\".");
+ }
- AffinityMask = 1 << ProcessorId;
+ PreferredCore = DefaultCpuCore;
- DynamicPriority = BasePriority = Priority;
+ AffinityMask |= 1L << DefaultCpuCore;
+
+ SchedFlags = Type == ThreadType.Dummy
+ ? ThreadSchedState.Running
+ : ThreadSchedState.None;
CurrentCore = PreferredCore;
+
+ DynamicPriority = Priority;
+ BasePriority = Priority;
+
+ ObjSyncResult = 0x7201;
+
+ this.Entrypoint = Entrypoint;
+
+ if (Type == ThreadType.User)
+ {
+ if (Owner.AllocateThreadLocalStorage(out TlsAddress) != KernelResult.Success)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ MemoryHelper.FillWithZeros(Owner.CpuMemory, (long)TlsAddress, KTlsPageInfo.TlsEntrySize);
+ }
+
+ bool Is64Bits;
+
+ if (Owner != null)
+ {
+ this.Owner = Owner;
+
+ Owner.IncrementThreadCount();
+
+ Is64Bits = (Owner.MmuFlags & 1) != 0;
+ }
+ else
+ {
+ Is64Bits = true;
+ }
+
+ Context = new CpuThread(Owner.Translator, Owner.CpuMemory, (long)Entrypoint);
+
+ Context.ThreadState.X0 = ArgsPtr;
+ Context.ThreadState.X31 = StackTop;
+
+ Context.ThreadState.CntfrqEl0 = 19200000;
+ Context.ThreadState.Tpidr = (long)TlsAddress;
+
+ Owner.SubscribeThreadEventHandlers(Context);
+
+ Context.WorkFinished += ThreadFinishedHandler;
+
+ ThreadUid = System.GetThreadUid();
+
+ if (Owner != null)
+ {
+ Owner.AddThread(this);
+
+ if (Owner.IsPaused)
+ {
+ System.CriticalSection.Enter();
+
+ if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
+ {
+ System.CriticalSection.Leave();
+
+ return KernelResult.Success;
+ }
+
+ ForcePauseFlags |= ThreadSchedState.ProcessPauseFlag;
+
+ CombineForcePauseFlags();
+
+ System.CriticalSection.Leave();
+ }
+ }
+
+ return KernelResult.Success;
}
- public long Start()
+ public KernelResult Start()
{
- long Result = MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
+ if (!System.KernelInitialized)
+ {
+ System.CriticalSection.Enter();
+
+ if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
+ {
+ ForcePauseFlags |= ThreadSchedState.KernelInitPauseFlag;
+
+ CombineForcePauseFlags();
+ }
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Leave();
+ }
+
+ KernelResult Result = KernelResult.ThreadTerminating;
+
+ System.CriticalSection.Enter();
if (!ShallBeTerminated)
{
@@ -106,9 +210,9 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.SchedFlags != ThreadSchedState.TerminationPending &&
!CurrentThread.ShallBeTerminated)
{
- if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.None)
+ if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None)
{
- Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
+ Result = KernelResult.InvalidState;
break;
}
@@ -122,7 +226,7 @@ namespace Ryujinx.HLE.HOS.Kernel
SetNewSchedFlags(ThreadSchedState.Running);
- Result = 0;
+ Result = KernelResult.Success;
break;
}
@@ -130,8 +234,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
CurrentThread.CombineForcePauseFlags();
- System.CriticalSectionLock.Unlock();
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Leave();
+ System.CriticalSection.Enter();
if (CurrentThread.ShallBeTerminated)
{
@@ -141,25 +245,25 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
public void Exit()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- ForcePauseFlags &= ~ThreadSchedState.ExceptionalMask;
+ ForcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
ExitImpl();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
private void ExitImpl()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
SetNewSchedFlags(ThreadSchedState.TerminationPending);
@@ -167,16 +271,16 @@ namespace Ryujinx.HLE.HOS.Kernel
Signal();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public long Sleep(long Timeout)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
}
@@ -188,7 +292,7 @@ namespace Ryujinx.HLE.HOS.Kernel
System.TimeManager.ScheduleFutureInvocation(this, Timeout);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
if (Timeout > 0)
{
@@ -200,11 +304,11 @@ namespace Ryujinx.HLE.HOS.Kernel
public void Yield()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
@@ -219,27 +323,27 @@ namespace Ryujinx.HLE.HOS.Kernel
Scheduler.ThreadReselectionRequested = true;
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
}
public void YieldWithLoadBalancing()
{
- System.CriticalSectionLock.Lock();
-
- int Prio = DynamicPriority;
- int Core = CurrentCore;
+ System.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
return;
}
+ int Prio = DynamicPriority;
+ int Core = CurrentCore;
+
KThread NextThreadOnCurrentQueue = null;
if (DynamicPriority < KScheduler.PrioritiesCount)
@@ -270,7 +374,7 @@ namespace Ryujinx.HLE.HOS.Kernel
//If the candidate was scheduled after the current thread, then it's not worth it,
//unless the priority is higher than the current one.
- if (NextThreadOnCurrentQueue.LastScheduledTicks >= Thread.LastScheduledTicks ||
+ if (NextThreadOnCurrentQueue.LastScheduledTime >= Thread.LastScheduledTime ||
NextThreadOnCurrentQueue.DynamicPriority < Thread.DynamicPriority)
{
yield return Thread;
@@ -292,18 +396,18 @@ namespace Ryujinx.HLE.HOS.Kernel
Scheduler.ThreadReselectionRequested = true;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
}
public void YieldAndWaitForLoadBalancing()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
@@ -348,47 +452,47 @@ namespace Ryujinx.HLE.HOS.Kernel
Scheduler.ThreadReselectionRequested = true;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
}
public void SetPriority(int Priority)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
BasePriority = Priority;
UpdatePriorityInheritance();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public long SetActivity(bool Pause)
{
long Result = 0;
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask;
+ ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask;
if (LowNibble != ThreadSchedState.Paused && LowNibble != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
{
if (Pause)
{
//Pause, the force pause flag should be clear (thread is NOT paused).
- if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) == 0)
+ if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0)
{
- ForcePauseFlags |= ThreadSchedState.ForcePauseFlag;
+ ForcePauseFlags |= ThreadSchedState.ThreadPauseFlag;
CombineForcePauseFlags();
}
@@ -400,17 +504,17 @@ namespace Ryujinx.HLE.HOS.Kernel
else
{
//Unpause, the force pause flag should be set (thread is paused).
- if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) != 0)
+ if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0)
{
ThreadSchedState OldForcePauseFlags = ForcePauseFlags;
- ForcePauseFlags &= ~ThreadSchedState.ForcePauseFlag;
+ ForcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag;
- if ((OldForcePauseFlags & ~ThreadSchedState.ForcePauseFlag) == ThreadSchedState.None)
+ if ((OldForcePauseFlags & ~ThreadSchedState.ThreadPauseFlag) == ThreadSchedState.None)
{
ThreadSchedState OldSchedFlags = SchedFlags;
- SchedFlags &= ThreadSchedState.LowNibbleMask;
+ SchedFlags &= ThreadSchedState.LowMask;
AdjustScheduling(OldSchedFlags);
}
@@ -422,27 +526,27 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
+ System.CriticalSection.Leave();
return Result;
}
public void CancelSynchronization()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.Paused || !WaitingSync)
+ if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.Paused || !WaitingSync)
{
SyncCancelled = true;
}
- else if (WithholderNode != null)
+ else if (Withholder != null)
{
- System.Withholders.Remove(WithholderNode);
+ Withholder.Remove(WithholderNode);
SetNewSchedFlags(ThreadSchedState.Running);
- WithholderNode = null;
+ Withholder = null;
SyncCancelled = true;
}
@@ -456,12 +560,12 @@ namespace Ryujinx.HLE.HOS.Kernel
SyncCancelled = false;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
- public long SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
+ public KernelResult SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
bool UseOverride = AffinityOverrideCount != 0;
@@ -472,9 +576,9 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((NewAffinityMask & (1 << NewCore)) == 0)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
- return MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
+ return KernelResult.InvalidCombination;
}
}
@@ -510,9 +614,9 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
- return 0;
+ return KernelResult.Success;
}
private static int HighestSetCore(long Mask)
@@ -531,7 +635,7 @@ namespace Ryujinx.HLE.HOS.Kernel
private void CombineForcePauseFlags()
{
ThreadSchedState OldFlags = SchedFlags;
- ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask;
+ ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask;
SchedFlags = LowNibble | ForcePauseFlags;
@@ -540,33 +644,33 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SetNewSchedFlags(ThreadSchedState NewFlags)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
ThreadSchedState OldFlags = SchedFlags;
- SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) | NewFlags;
+ SchedFlags = (OldFlags & ThreadSchedState.HighMask) | NewFlags;
- if ((OldFlags & ThreadSchedState.LowNibbleMask) != NewFlags)
+ if ((OldFlags & ThreadSchedState.LowMask) != NewFlags)
{
AdjustScheduling(OldFlags);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public void ReleaseAndResume()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- if ((SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused)
+ if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
{
- if (WithholderNode != null)
+ if (Withholder != null)
{
- System.Withholders.Remove(WithholderNode);
+ Withholder.Remove(WithholderNode);
SetNewSchedFlags(ThreadSchedState.Running);
- WithholderNode = null;
+ Withholder = null;
}
else
{
@@ -574,21 +678,21 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public void Reschedule(ThreadSchedState NewFlags)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
ThreadSchedState OldFlags = SchedFlags;
- SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) |
- (NewFlags & ThreadSchedState.LowNibbleMask);
+ SchedFlags = (OldFlags & ThreadSchedState.HighMask) |
+ (NewFlags & ThreadSchedState.LowMask);
AdjustScheduling(OldFlags);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public void AddMutexWaiter(KThread Requester)
@@ -866,18 +970,61 @@ namespace Ryujinx.HLE.HOS.Kernel
return HasExited;
}
+ public void SetEntryArguments(long ArgsPtr, int ThreadHandle)
+ {
+ Context.ThreadState.X0 = (ulong)ArgsPtr;
+ Context.ThreadState.X1 = (ulong)ThreadHandle;
+ }
+
public void ClearExclusive()
{
- Owner.Memory.ClearExclusive(CurrentCore);
+ Owner.CpuMemory.ClearExclusive(CurrentCore);
}
public void TimeUp()
{
- System.CriticalSectionLock.Lock();
+ ReleaseAndResume();
+ }
+
+ public void PrintGuestStackTrace()
+ {
+ Owner.Debugger.PrintGuestStackTrace(Context.ThreadState);
+ }
+
+ private void ThreadFinishedHandler(object sender, EventArgs e)
+ {
+ System.Scheduler.ExitThread(this);
+
+ Terminate();
+
+ System.Scheduler.RemoveThread(this);
+ }
+
+ public void Terminate()
+ {
+ Owner?.RemoveThread(this);
+
+ if (TlsAddress != 0 && Owner.FreeThreadLocalStorage(TlsAddress) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure freeing thread local storage.");
+ }
+
+ System.CriticalSection.Enter();
+
+ //Wake up all threads that may be waiting for a mutex being held
+ //by this thread.
+ foreach (KThread Thread in MutexWaiters)
+ {
+ Thread.MutexOwner = null;
+ Thread.PreferredCoreOverride = 0;
+ Thread.ObjSyncResult = 0xfa01;
+
+ Thread.ReleaseAndResume();
+ }
- SetNewSchedFlags(ThreadSchedState.Running);
+ System.CriticalSection.Leave();
- System.CriticalSectionLock.Unlock();
+ Owner?.DecrementThreadCountAndTerminateIfZero();
}
}
} \ No newline at end of file