aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/KProcess.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/KProcess.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/KProcess.cs')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KProcess.cs1013
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