diff options
author | gdkchan <gab.dark.100@gmail.com> | 2018-11-28 20:18:09 -0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-28 20:18:09 -0200 |
commit | 00579927e43bf55ee06ae02933c1e755fb4120eb (patch) | |
tree | 0fd06db7b28e0accf87b465ec6f4dc74691febab /Ryujinx.HLE/HOS/Kernel/KThread.cs | |
parent | e7fe7d724778535f8eff390abef54274a343c0b7 (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.cs | 337 |
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 |