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/KProcess.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/KProcess.cs')
-rw-r--r-- | Ryujinx.HLE/HOS/Kernel/KProcess.cs | 1013 |
1 files changed, 1013 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/KProcess.cs new file mode 100644 index 00000000..094ef222 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KProcess.cs @@ -0,0 +1,1013 @@ +using ChocolArm64; +using ChocolArm64.Events; +using ChocolArm64.Memory; +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KProcess : KSynchronizationObject + { + public const int KernelVersionMajor = 10; + public const int KernelVersionMinor = 4; + public const int KernelVersionRevision = 0; + + public const int KernelVersionPacked = + (KernelVersionMajor << 19) | + (KernelVersionMinor << 15) | + (KernelVersionRevision << 0); + + public KMemoryManager MemoryManager { get; private set; } + + private SortedDictionary<ulong, KTlsPageInfo> FullTlsPages; + private SortedDictionary<ulong, KTlsPageInfo> FreeTlsPages; + + public int DefaultCpuCore { get; private set; } + + public bool Debug { get; private set; } + + public KResourceLimit ResourceLimit { get; private set; } + + public ulong PersonalMmHeapPagesCount { get; private set; } + + private ProcessState State; + + private object ProcessLock; + private object ThreadingLock; + + public KAddressArbiter AddressArbiter { get; private set; } + + public long[] RandomEntropy { get; private set; } + + private bool Signaled; + private bool UseSystemMemBlocks; + + public string Name { get; private set; } + + private int ThreadCount; + + public int MmuFlags { get; private set; } + + private MemoryRegion MemRegion; + + public KProcessCapabilities Capabilities { get; private set; } + + public long TitleId { get; private set; } + public long Pid { get; private set; } + + private long CreationTimestamp; + private ulong Entrypoint; + private ulong ImageSize; + private ulong MainThreadStackSize; + private ulong MemoryUsageCapacity; + private int Category; + + public KHandleTable HandleTable { get; private set; } + + public ulong UserExceptionContextAddress { get; private set; } + + private LinkedList<KThread> Threads; + + public bool IsPaused { get; private set; } + + public Translator Translator { get; private set; } + + public MemoryManager CpuMemory { get; private set; } + + private SvcHandler SvcHandler; + + public HleProcessDebugger Debugger { get; private set; } + + public KProcess(Horizon System) : base(System) + { + ProcessLock = new object(); + ThreadingLock = new object(); + + CpuMemory = new MemoryManager(System.Device.Memory.RamPointer); + + CpuMemory.InvalidAccess += InvalidAccessHandler; + + AddressArbiter = new KAddressArbiter(System); + + MemoryManager = new KMemoryManager(System, CpuMemory); + + FullTlsPages = new SortedDictionary<ulong, KTlsPageInfo>(); + FreeTlsPages = new SortedDictionary<ulong, KTlsPageInfo>(); + + Capabilities = new KProcessCapabilities(); + + RandomEntropy = new long[KScheduler.CpuCoresCount]; + + Threads = new LinkedList<KThread>(); + + Translator = new Translator(); + + Translator.CpuTrace += CpuTraceHandler; + + SvcHandler = new SvcHandler(System.Device, this); + + Debugger = new HleProcessDebugger(this); + } + + public KernelResult InitializeKip( + ProcessCreationInfo CreationInfo, + int[] Caps, + KPageList PageList, + KResourceLimit ResourceLimit, + MemoryRegion MemRegion) + { + this.ResourceLimit = ResourceLimit; + this.MemRegion = MemRegion; + + AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7); + + bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0; + + ulong CodeAddress = CreationInfo.CodeAddress; + + ulong CodeSize = (ulong)CreationInfo.CodePagesCount * KMemoryManager.PageSize; + + KMemoryBlockAllocator MemoryBlockAllocator = (MmuFlags & 0x40) != 0 + ? System.LargeMemoryBlockAllocator + : System.SmallMemoryBlockAllocator; + + KernelResult Result = MemoryManager.InitializeForProcess( + AddrSpaceType, + AslrEnabled, + !AslrEnabled, + MemRegion, + CodeAddress, + CodeSize, + MemoryBlockAllocator); + + if (Result != KernelResult.Success) + { + return Result; + } + + if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize)) + { + return KernelResult.InvalidMemRange; + } + + Result = MemoryManager.MapPages( + CodeAddress, + PageList, + MemoryState.CodeStatic, + MemoryPermission.None); + + if (Result != KernelResult.Success) + { + return Result; + } + + Result = Capabilities.InitializeForKernel(Caps, MemoryManager); + + if (Result != KernelResult.Success) + { + return Result; + } + + Pid = System.GetKipId(); + + if (Pid == 0 || (ulong)Pid >= Horizon.InitialProcessId) + { + throw new InvalidOperationException($"Invalid KIP Id {Pid}."); + } + + Result = ParseProcessInfo(CreationInfo); + + return Result; + } + + public KernelResult Initialize( + ProcessCreationInfo CreationInfo, + int[] Caps, + KResourceLimit ResourceLimit, + MemoryRegion MemRegion) + { + this.ResourceLimit = ResourceLimit; + this.MemRegion = MemRegion; + + ulong PersonalMmHeapSize = GetPersonalMmHeapSize((ulong)CreationInfo.PersonalMmHeapPagesCount, MemRegion); + + ulong CodePagesCount = (ulong)CreationInfo.CodePagesCount; + + ulong NeededSizeForProcess = PersonalMmHeapSize + CodePagesCount * KMemoryManager.PageSize; + + if (NeededSizeForProcess != 0 && ResourceLimit != null) + { + if (!ResourceLimit.Reserve(LimitableResource.Memory, NeededSizeForProcess)) + { + return KernelResult.ResLimitExceeded; + } + } + + void CleanUpForError() + { + if (NeededSizeForProcess != 0 && ResourceLimit != null) + { + ResourceLimit.Release(LimitableResource.Memory, NeededSizeForProcess); + } + } + + PersonalMmHeapPagesCount = (ulong)CreationInfo.PersonalMmHeapPagesCount; + + KMemoryBlockAllocator MemoryBlockAllocator; + + if (PersonalMmHeapPagesCount != 0) + { + MemoryBlockAllocator = new KMemoryBlockAllocator(PersonalMmHeapPagesCount * KMemoryManager.PageSize); + } + else + { + MemoryBlockAllocator = (MmuFlags & 0x40) != 0 + ? System.LargeMemoryBlockAllocator + : System.SmallMemoryBlockAllocator; + } + + AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7); + + bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0; + + ulong CodeAddress = CreationInfo.CodeAddress; + + ulong CodeSize = CodePagesCount * KMemoryManager.PageSize; + + KernelResult Result = MemoryManager.InitializeForProcess( + AddrSpaceType, + AslrEnabled, + !AslrEnabled, + MemRegion, + CodeAddress, + CodeSize, + MemoryBlockAllocator); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize)) + { + CleanUpForError(); + + return KernelResult.InvalidMemRange; + } + + Result = MemoryManager.MapNewProcessCode( + CodeAddress, + CodePagesCount, + MemoryState.CodeStatic, + MemoryPermission.None); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + Result = Capabilities.InitializeForUser(Caps, MemoryManager); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + Pid = System.GetProcessId(); + + if (Pid == -1 || (ulong)Pid < Horizon.InitialProcessId) + { + throw new InvalidOperationException($"Invalid Process Id {Pid}."); + } + + Result = ParseProcessInfo(CreationInfo); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + } + + return Result; + } + + private bool ValidateCodeAddressAndSize(ulong Address, ulong Size) + { + ulong CodeRegionStart; + ulong CodeRegionSize; + + switch (MemoryManager.AddrSpaceWidth) + { + case 32: + CodeRegionStart = 0x200000; + CodeRegionSize = 0x3fe00000; + break; + + case 36: + CodeRegionStart = 0x8000000; + CodeRegionSize = 0x78000000; + break; + + case 39: + CodeRegionStart = 0x8000000; + CodeRegionSize = 0x7ff8000000; + break; + + default: throw new InvalidOperationException("Invalid address space width on memory manager."); + } + + ulong EndAddr = Address + Size; + + ulong CodeRegionEnd = CodeRegionStart + CodeRegionSize; + + if (EndAddr <= Address || + EndAddr - 1 > CodeRegionEnd - 1) + { + return false; + } + + if (MemoryManager.InsideHeapRegion (Address, Size) || + MemoryManager.InsideAliasRegion(Address, Size)) + { + return false; + } + + return true; + } + + private KernelResult ParseProcessInfo(ProcessCreationInfo CreationInfo) + { + //Ensure that the current kernel version is equal or above to the minimum required. + uint RequiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19; + uint RequiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf; + + if (System.EnableVersionChecks) + { + if (RequiredKernelVersionMajor > KernelVersionMajor) + { + return KernelResult.InvalidCombination; + } + + if (RequiredKernelVersionMajor != KernelVersionMajor && RequiredKernelVersionMajor < 3) + { + return KernelResult.InvalidCombination; + } + + if (RequiredKernelVersionMinor > KernelVersionMinor) + { + return KernelResult.InvalidCombination; + } + } + + KernelResult Result = AllocateThreadLocalStorage(out ulong UserExceptionContextAddress); + + if (Result != KernelResult.Success) + { + return Result; + } + + this.UserExceptionContextAddress = UserExceptionContextAddress; + + MemoryHelper.FillWithZeros(CpuMemory, (long)UserExceptionContextAddress, KTlsPageInfo.TlsEntrySize); + + Name = CreationInfo.Name; + + State = ProcessState.Created; + + CreationTimestamp = PerformanceCounter.ElapsedMilliseconds; + + MmuFlags = CreationInfo.MmuFlags; + Category = CreationInfo.Category; + TitleId = CreationInfo.TitleId; + Entrypoint = CreationInfo.CodeAddress; + ImageSize = (ulong)CreationInfo.CodePagesCount * KMemoryManager.PageSize; + + UseSystemMemBlocks = ((MmuFlags >> 6) & 1) != 0; + + switch ((AddressSpaceType)((MmuFlags >> 1) & 7)) + { + case AddressSpaceType.Addr32Bits: + case AddressSpaceType.Addr36Bits: + case AddressSpaceType.Addr39Bits: + MemoryUsageCapacity = MemoryManager.HeapRegionEnd - + MemoryManager.HeapRegionStart; + break; + + case AddressSpaceType.Addr32BitsNoMap: + MemoryUsageCapacity = MemoryManager.HeapRegionEnd - + MemoryManager.HeapRegionStart + + MemoryManager.AliasRegionEnd - + MemoryManager.AliasRegionStart; + break; + + default: throw new InvalidOperationException($"Invalid MMU flags value 0x{MmuFlags:x2}."); + } + + GenerateRandomEntropy(); + + return KernelResult.Success; + } + + public KernelResult AllocateThreadLocalStorage(out ulong Address) + { + System.CriticalSection.Enter(); + + KernelResult Result; + + if (FreeTlsPages.Count > 0) + { + //If we have free TLS pages available, just use the first one. + KTlsPageInfo PageInfo = FreeTlsPages.Values.First(); + + if (!PageInfo.TryGetFreePage(out Address)) + { + throw new InvalidOperationException("Unexpected failure getting free TLS page!"); + } + + if (PageInfo.IsFull()) + { + FreeTlsPages.Remove(PageInfo.PageAddr); + + FullTlsPages.Add(PageInfo.PageAddr, PageInfo); + } + + Result = KernelResult.Success; + } + else + { + //Otherwise, we need to create a new one. + Result = AllocateTlsPage(out KTlsPageInfo PageInfo); + + if (Result == KernelResult.Success) + { + if (!PageInfo.TryGetFreePage(out Address)) + { + throw new InvalidOperationException("Unexpected failure getting free TLS page!"); + } + + FreeTlsPages.Add(PageInfo.PageAddr, PageInfo); + } + else + { + Address = 0; + } + } + + System.CriticalSection.Leave(); + + return Result; + } + + private KernelResult AllocateTlsPage(out KTlsPageInfo PageInfo) + { + PageInfo = default(KTlsPageInfo); + + if (!System.UserSlabHeapPages.TryGetItem(out ulong TlsPagePa)) + { + return KernelResult.OutOfMemory; + } + + ulong RegionStart = MemoryManager.TlsIoRegionStart; + ulong RegionSize = MemoryManager.TlsIoRegionEnd - RegionStart; + + ulong RegionPagesCount = RegionSize / KMemoryManager.PageSize; + + KernelResult Result = MemoryManager.AllocateOrMapPa( + 1, + KMemoryManager.PageSize, + TlsPagePa, + true, + RegionStart, + RegionPagesCount, + MemoryState.ThreadLocal, + MemoryPermission.ReadAndWrite, + out ulong TlsPageVa); + + if (Result != KernelResult.Success) + { + System.UserSlabHeapPages.Free(TlsPagePa); + } + else + { + PageInfo = new KTlsPageInfo(TlsPageVa); + + MemoryHelper.FillWithZeros(CpuMemory, (long)TlsPageVa, KMemoryManager.PageSize); + } + + return Result; + } + + public KernelResult FreeThreadLocalStorage(ulong TlsSlotAddr) + { + ulong TlsPageAddr = BitUtils.AlignDown(TlsSlotAddr, KMemoryManager.PageSize); + + System.CriticalSection.Enter(); + + KernelResult Result = KernelResult.Success; + + KTlsPageInfo PageInfo = null; + + if (FullTlsPages.TryGetValue(TlsPageAddr, out PageInfo)) + { + //TLS page was full, free slot and move to free pages tree. + FullTlsPages.Remove(TlsPageAddr); + + FreeTlsPages.Add(TlsPageAddr, PageInfo); + } + else if (!FreeTlsPages.TryGetValue(TlsPageAddr, out PageInfo)) + { + Result = KernelResult.InvalidAddress; + } + + if (PageInfo != null) + { + PageInfo.FreeTlsSlot(TlsSlotAddr); + + if (PageInfo.IsEmpty()) + { + //TLS page is now empty, we should ensure it is removed + //from all trees, and free the memory it was using. + FreeTlsPages.Remove(TlsPageAddr); + + System.CriticalSection.Leave(); + + FreeTlsPage(PageInfo); + + return KernelResult.Success; + } + } + + System.CriticalSection.Leave(); + + return Result; + } + + private KernelResult FreeTlsPage(KTlsPageInfo PageInfo) + { + KernelResult Result = MemoryManager.ConvertVaToPa(PageInfo.PageAddr, out ulong TlsPagePa); + + if (Result != KernelResult.Success) + { + throw new InvalidOperationException("Unexpected failure translating virtual address to physical."); + } + + Result = MemoryManager.UnmapForKernel(PageInfo.PageAddr, 1, MemoryState.ThreadLocal); + + if (Result == KernelResult.Success) + { + System.UserSlabHeapPages.Free(TlsPagePa); + } + + return Result; + } + + private void GenerateRandomEntropy() + { + //TODO. + } + + public KernelResult Start(int MainThreadPriority, ulong StackSize) + { + lock (ProcessLock) + { + if (State > ProcessState.CreatedAttached) + { + return KernelResult.InvalidState; + } + + if (ResourceLimit != null && !ResourceLimit.Reserve(LimitableResource.Thread, 1)) + { + return KernelResult.ResLimitExceeded; + } + + KResourceLimit ThreadResourceLimit = ResourceLimit; + KResourceLimit MemoryResourceLimit = null; + + if (MainThreadStackSize != 0) + { + throw new InvalidOperationException("Trying to start a process with a invalid state!"); + } + + ulong StackSizeRounded = BitUtils.AlignUp(StackSize, KMemoryManager.PageSize); + + ulong NeededSize = StackSizeRounded + ImageSize; + + //Check if the needed size for the code and the stack will fit on the + //memory usage capacity of this Process. Also check for possible overflow + //on the above addition. + if (NeededSize > MemoryUsageCapacity || + NeededSize < StackSizeRounded) + { + ThreadResourceLimit?.Release(LimitableResource.Thread, 1); + + return KernelResult.OutOfMemory; + } + + if (StackSizeRounded != 0 && ResourceLimit != null) + { + MemoryResourceLimit = ResourceLimit; + + if (!MemoryResourceLimit.Reserve(LimitableResource.Memory, StackSizeRounded)) + { + ThreadResourceLimit?.Release(LimitableResource.Thread, 1); + + return KernelResult.ResLimitExceeded; + } + } + + KernelResult Result; + + KThread MainThread = null; + + ulong StackTop = 0; + + void CleanUpForError() + { + MainThread?.Terminate(); + HandleTable.Destroy(); + + if (MainThreadStackSize != 0) + { + ulong StackBottom = StackTop - MainThreadStackSize; + + ulong StackPagesCount = MainThreadStackSize / KMemoryManager.PageSize; + + MemoryManager.UnmapForKernel(StackBottom, StackPagesCount, MemoryState.Stack); + } + + MemoryResourceLimit?.Release(LimitableResource.Memory, StackSizeRounded); + ThreadResourceLimit?.Release(LimitableResource.Thread, 1); + } + + if (StackSizeRounded != 0) + { + ulong StackPagesCount = StackSizeRounded / KMemoryManager.PageSize; + + ulong RegionStart = MemoryManager.StackRegionStart; + ulong RegionSize = MemoryManager.StackRegionEnd - RegionStart; + + ulong RegionPagesCount = RegionSize / KMemoryManager.PageSize; + + Result = MemoryManager.AllocateOrMapPa( + StackPagesCount, + KMemoryManager.PageSize, + 0, + false, + RegionStart, + RegionPagesCount, + MemoryState.Stack, + MemoryPermission.ReadAndWrite, + out ulong StackBottom); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + MainThreadStackSize += StackSizeRounded; + + StackTop = StackBottom + StackSizeRounded; + } + + ulong HeapCapacity = MemoryUsageCapacity - MainThreadStackSize - ImageSize; + + Result = MemoryManager.SetHeapCapacity(HeapCapacity); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + HandleTable = new KHandleTable(System); + + Result = HandleTable.Initialize(Capabilities.HandleTableSize); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + MainThread = new KThread(System); + + Result = MainThread.Initialize( + Entrypoint, + 0, + StackTop, + MainThreadPriority, + DefaultCpuCore, + this); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + Result = HandleTable.GenerateHandle(MainThread, out int MainThreadHandle); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + MainThread.SetEntryArguments(0, MainThreadHandle); + + ProcessState OldState = State; + ProcessState NewState = State != ProcessState.Created + ? ProcessState.Attached + : ProcessState.Started; + + SetState(NewState); + + //TODO: We can't call KThread.Start from a non-guest thread. + //We will need to make some changes to allow the creation of + //dummy threads that will be used to initialize the current + //thread on KCoreContext so that GetCurrentThread doesn't fail. + /* Result = MainThread.Start(); + + if (Result != KernelResult.Success) + { + SetState(OldState); + + CleanUpForError(); + } */ + + MainThread.Reschedule(ThreadSchedState.Running); + + return Result; + } + } + + private void SetState(ProcessState NewState) + { + if (State != NewState) + { + State = NewState; + Signaled = true; + + Signal(); + } + } + + public KernelResult InitializeThread( + KThread Thread, + ulong Entrypoint, + ulong ArgsPtr, + ulong StackTop, + int Priority, + int CpuCore) + { + lock (ProcessLock) + { + return Thread.Initialize(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, this); + } + } + + public void SubscribeThreadEventHandlers(CpuThread Context) + { + Context.ThreadState.Interrupt += InterruptHandler; + Context.ThreadState.SvcCall += SvcHandler.SvcCall; + } + + private void InterruptHandler(object sender, EventArgs e) + { + System.Scheduler.ContextSwitch(); + } + + public void IncrementThreadCount() + { + Interlocked.Increment(ref ThreadCount); + + System.ThreadCounter.AddCount(); + } + + public void DecrementThreadCountAndTerminateIfZero() + { + System.ThreadCounter.Signal(); + + if (Interlocked.Decrement(ref ThreadCount) == 0) + { + Terminate(); + } + } + + public ulong GetMemoryCapacity() + { + ulong TotalCapacity = (ulong)ResourceLimit.GetRemainingValue(LimitableResource.Memory); + + TotalCapacity += MemoryManager.GetTotalHeapSize(); + + TotalCapacity += GetPersonalMmHeapSize(); + + TotalCapacity += ImageSize + MainThreadStackSize; + + if (TotalCapacity <= MemoryUsageCapacity) + { + return TotalCapacity; + } + + return MemoryUsageCapacity; + } + + public ulong GetMemoryUsage() + { + return ImageSize + MainThreadStackSize + MemoryManager.GetTotalHeapSize() + GetPersonalMmHeapSize(); + } + + public ulong GetMemoryCapacityWithoutPersonalMmHeap() + { + return GetMemoryCapacity() - GetPersonalMmHeapSize(); + } + + public ulong GetMemoryUsageWithoutPersonalMmHeap() + { + return GetMemoryUsage() - GetPersonalMmHeapSize(); + } + + private ulong GetPersonalMmHeapSize() + { + return GetPersonalMmHeapSize(PersonalMmHeapPagesCount, MemRegion); + } + + private static ulong GetPersonalMmHeapSize(ulong PersonalMmHeapPagesCount, MemoryRegion MemRegion) + { + if (MemRegion == MemoryRegion.Applet) + { + return 0; + } + + return PersonalMmHeapPagesCount * KMemoryManager.PageSize; + } + + public void AddThread(KThread Thread) + { + lock (ThreadingLock) + { + Thread.ProcessListNode = Threads.AddLast(Thread); + } + } + + public void RemoveThread(KThread Thread) + { + lock (ThreadingLock) + { + Threads.Remove(Thread.ProcessListNode); + } + } + + public bool IsCpuCoreAllowed(int Core) + { + return (Capabilities.AllowedCpuCoresMask & (1L << Core)) != 0; + } + + public bool IsPriorityAllowed(int Priority) + { + return (Capabilities.AllowedThreadPriosMask & (1L << Priority)) != 0; + } + + public override bool IsSignaled() + { + return Signaled; + } + + public KernelResult Terminate() + { + KernelResult Result; + + bool ShallTerminate = false; + + System.CriticalSection.Enter(); + + lock (ProcessLock) + { + if (State >= ProcessState.Started) + { + if (State == ProcessState.Started || + State == ProcessState.Crashed || + State == ProcessState.Attached || + State == ProcessState.DebugSuspended) + { + SetState(ProcessState.Exiting); + + ShallTerminate = true; + } + + Result = KernelResult.Success; + } + else + { + Result = KernelResult.InvalidState; + } + } + + System.CriticalSection.Leave(); + + if (ShallTerminate) + { + //UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread()); + + HandleTable.Destroy(); + + SignalExitForDebugEvent(); + SignalExit(); + } + + return Result; + } + + private void UnpauseAndTerminateAllThreadsExcept(KThread Thread) + { + //TODO. + } + + private void SignalExitForDebugEvent() + { + //TODO: Debug events. + } + + private void SignalExit() + { + if (ResourceLimit != null) + { + ResourceLimit.Release(LimitableResource.Memory, GetMemoryUsage()); + } + + System.CriticalSection.Enter(); + + SetState(ProcessState.Exited); + + System.CriticalSection.Leave(); + } + + public KernelResult ClearIfNotExited() + { + KernelResult Result; + + System.CriticalSection.Enter(); + + lock (ProcessLock) + { + if (State != ProcessState.Exited && Signaled) + { + Signaled = false; + + Result = KernelResult.Success; + } + else + { + Result = KernelResult.InvalidState; + } + } + + System.CriticalSection.Leave(); + + return Result; + } + + public void StopAllThreads() + { + lock (ThreadingLock) + { + foreach (KThread Thread in Threads) + { + Thread.Context.StopExecution(); + + System.Scheduler.CoreManager.Set(Thread.Context.Work); + } + } + } + + private void InvalidAccessHandler(object sender, InvalidAccessEventArgs e) + { + PrintCurrentThreadStackTrace(); + } + + public void PrintCurrentThreadStackTrace() + { + System.Scheduler.GetCurrentThread().PrintGuestStackTrace(); + } + + private void CpuTraceHandler(object sender, CpuTraceEventArgs e) + { + Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}."); + } + } +}
\ No newline at end of file |