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/KProcessCapabilities.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/KProcessCapabilities.cs')
-rw-r--r-- | Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs new file mode 100644 index 00000000..dfbe1f36 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs @@ -0,0 +1,311 @@ +using Ryujinx.Common; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KProcessCapabilities + { + public byte[] SvcAccessMask { get; private set; } + public byte[] IrqAccessMask { get; private set; } + + public long AllowedCpuCoresMask { get; private set; } + public long AllowedThreadPriosMask { get; private set; } + + public int DebuggingFlags { get; private set; } + public int HandleTableSize { get; private set; } + public int KernelReleaseVersion { get; private set; } + public int ApplicationType { get; private set; } + + public KProcessCapabilities() + { + SvcAccessMask = new byte[0x10]; + IrqAccessMask = new byte[0x80]; + } + + public KernelResult InitializeForKernel(int[] Caps, KMemoryManager MemoryManager) + { + AllowedCpuCoresMask = 0xf; + AllowedThreadPriosMask = -1; + DebuggingFlags &= ~3; + KernelReleaseVersion = KProcess.KernelVersionPacked; + + return Parse(Caps, MemoryManager); + } + + public KernelResult InitializeForUser(int[] Caps, KMemoryManager MemoryManager) + { + return Parse(Caps, MemoryManager); + } + + private KernelResult Parse(int[] Caps, KMemoryManager MemoryManager) + { + int Mask0 = 0; + int Mask1 = 0; + + for (int Index = 0; Index < Caps.Length; Index++) + { + int Cap = Caps[Index]; + + if (((Cap + 1) & ~Cap) != 0x40) + { + KernelResult Result = ParseCapability(Cap, ref Mask0, ref Mask1, MemoryManager); + + if (Result != KernelResult.Success) + { + return Result; + } + } + else + { + if ((uint)Index + 1 >= Caps.Length) + { + return KernelResult.InvalidCombination; + } + + int PrevCap = Cap; + + Cap = Caps[++Index]; + + if (((Cap + 1) & ~Cap) != 0x40) + { + return KernelResult.InvalidCombination; + } + + if ((Cap & 0x78000000) != 0) + { + return KernelResult.MaximumExceeded; + } + + if ((Cap & 0x7ffff80) == 0) + { + return KernelResult.InvalidSize; + } + + long Address = ((long)(uint)PrevCap << 5) & 0xffffff000; + long Size = ((long)(uint)Cap << 5) & 0xfffff000; + + if (((ulong)(Address + Size - 1) >> 36) != 0) + { + return KernelResult.InvalidAddress; + } + + MemoryPermission Perm = (PrevCap >> 31) != 0 + ? MemoryPermission.Read + : MemoryPermission.ReadAndWrite; + + KernelResult Result; + + if ((Cap >> 31) != 0) + { + Result = MemoryManager.MapNormalMemory(Address, Size, Perm); + } + else + { + Result = MemoryManager.MapIoMemory(Address, Size, Perm); + } + + if (Result != KernelResult.Success) + { + return Result; + } + } + } + + return KernelResult.Success; + } + + private KernelResult ParseCapability(int Cap, ref int Mask0, ref int Mask1, KMemoryManager MemoryManager) + { + int Code = (Cap + 1) & ~Cap; + + if (Code == 1) + { + return KernelResult.InvalidCapability; + } + else if (Code == 0) + { + return KernelResult.Success; + } + + int CodeMask = 1 << (32 - BitUtils.CountLeadingZeros32(Code + 1)); + + //Check if the property was already set. + if (((Mask0 & CodeMask) & 0x1e008) != 0) + { + return KernelResult.InvalidCombination; + } + + Mask0 |= CodeMask; + + switch (Code) + { + case 8: + { + if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0) + { + return KernelResult.InvalidCapability; + } + + int LowestCpuCore = (Cap >> 16) & 0xff; + int HighestCpuCore = (Cap >> 24) & 0xff; + + if (LowestCpuCore > HighestCpuCore) + { + return KernelResult.InvalidCombination; + } + + int HighestThreadPrio = (Cap >> 4) & 0x3f; + int LowestThreadPrio = (Cap >> 10) & 0x3f; + + if (LowestThreadPrio > HighestThreadPrio) + { + return KernelResult.InvalidCombination; + } + + if (HighestCpuCore >= KScheduler.CpuCoresCount) + { + return KernelResult.InvalidCpuCore; + } + + AllowedCpuCoresMask = GetMaskFromMinMax(LowestCpuCore, HighestCpuCore); + AllowedThreadPriosMask = GetMaskFromMinMax(LowestThreadPrio, HighestThreadPrio); + + break; + } + + case 0x10: + { + int Slot = (Cap >> 29) & 7; + + int SvcSlotMask = 1 << Slot; + + if ((Mask1 & SvcSlotMask) != 0) + { + return KernelResult.InvalidCombination; + } + + Mask1 |= SvcSlotMask; + + int SvcMask = (Cap >> 5) & 0xffffff; + + int BaseSvc = Slot * 24; + + for (int Index = 0; Index < 24; Index++) + { + if (((SvcMask >> Index) & 1) == 0) + { + continue; + } + + int SvcId = BaseSvc + Index; + + if (SvcId > 0x7f) + { + return KernelResult.MaximumExceeded; + } + + SvcAccessMask[SvcId / 8] |= (byte)(1 << (SvcId & 7)); + } + + break; + } + + case 0x80: + { + long Address = ((long)(uint)Cap << 4) & 0xffffff000; + + MemoryManager.MapIoMemory(Address, KMemoryManager.PageSize, MemoryPermission.ReadAndWrite); + + break; + } + + case 0x800: + { + //TODO: GIC distributor check. + int Irq0 = (Cap >> 12) & 0x3ff; + int Irq1 = (Cap >> 22) & 0x3ff; + + if (Irq0 != 0x3ff) + { + IrqAccessMask[Irq0 / 8] |= (byte)(1 << (Irq0 & 7)); + } + + if (Irq1 != 0x3ff) + { + IrqAccessMask[Irq1 / 8] |= (byte)(1 << (Irq1 & 7)); + } + + break; + } + + case 0x2000: + { + int ApplicationType = Cap >> 14; + + if ((uint)ApplicationType > 7) + { + return KernelResult.ReservedValue; + } + + this.ApplicationType = ApplicationType; + + break; + } + + case 0x4000: + { + //Note: This check is bugged on kernel too, we are just replicating the bug here. + if ((KernelReleaseVersion >> 17) != 0 || Cap < 0x80000) + { + return KernelResult.ReservedValue; + } + + KernelReleaseVersion = Cap; + + break; + } + + case 0x8000: + { + int HandleTableSize = Cap >> 26; + + if ((uint)HandleTableSize > 0x3ff) + { + return KernelResult.ReservedValue; + } + + this.HandleTableSize = HandleTableSize; + + break; + } + + case 0x10000: + { + int DebuggingFlags = Cap >> 19; + + if ((uint)DebuggingFlags > 3) + { + return KernelResult.ReservedValue; + } + + this.DebuggingFlags &= ~3; + this.DebuggingFlags |= DebuggingFlags; + + break; + } + + default: return KernelResult.InvalidCapability; + } + + return KernelResult.Success; + } + + private static long GetMaskFromMinMax(int Min, int Max) + { + int Range = Max - Min + 1; + + long Mask = (1L << Range) - 1; + + return Mask << Min; + } + } +}
\ No newline at end of file |