aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-12-18 03:33:36 -0200
committerGitHub <noreply@github.com>2018-12-18 03:33:36 -0200
commit0039bb639493b2d1e2764cae380311ba8e87704b (patch)
tree63a912a95c8261775c2acb8a5b9ca0f10ad4ae33 /Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
parent2534a7f10c627810e6e0272b4cc9758e90f733c1 (diff)
Refactor SVC handler (#540)
* Refactor SVC handler * Get rid of KernelErr * Split kernel code files into multiple folders
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs1017
1 files changed, 1017 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
new file mode 100644
index 00000000..0d77a495
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
@@ -0,0 +1,1017 @@
+using ChocolArm64;
+using ChocolArm64.Events;
+using ChocolArm64.Memory;
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Memory;
+using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel.Process
+{
+ 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)
+ {
+ ResourceLimit = resourceLimit;
+ _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)
+ {
+ ResourceLimit = resourceLimit;
+ _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;
+ }
+
+ 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, MemoryAccessEventArgs 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