diff options
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs')
-rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs new file mode 100644 index 00000000..c99e3112 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -0,0 +1,328 @@ +using Ryujinx.HLE.HOS.Kernel.Memory; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; +using System; +using System.Numerics; + +namespace Ryujinx.HLE.HOS.Kernel.Process +{ + class KProcessCapabilities + { + public byte[] SvcAccessMask { get; } + public byte[] IrqAccessMask { get; } + + public ulong AllowedCpuCoresMask { get; private set; } + public ulong AllowedThreadPriosMask { get; private set; } + + public uint DebuggingFlags { get; private set; } + public uint HandleTableSize { get; private set; } + public uint KernelReleaseVersion { get; private set; } + public uint ApplicationType { get; private set; } + + public KProcessCapabilities() + { + // length / number of bits of the underlying type + SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / 8]; + IrqAccessMask = new byte[0x80]; + } + + public Result InitializeForKernel(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager) + { + AllowedCpuCoresMask = 0xf; + AllowedThreadPriosMask = ulong.MaxValue; + DebuggingFlags &= ~3u; + KernelReleaseVersion = KProcess.KernelVersionPacked; + + return Parse(capabilities, memoryManager); + } + + public Result InitializeForUser(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager) + { + return Parse(capabilities, memoryManager); + } + + private Result Parse(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager) + { + int mask0 = 0; + int mask1 = 0; + + for (int index = 0; index < capabilities.Length; index++) + { + uint cap = capabilities[index]; + + if (cap.GetCapabilityType() != CapabilityType.MapRange) + { + Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager); + + if (result != Result.Success) + { + return result; + } + } + else + { + if ((uint)index + 1 >= capabilities.Length) + { + return KernelResult.InvalidCombination; + } + + uint prevCap = cap; + + cap = capabilities[++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)prevCap << 5) & 0xffffff000; + long size = ((long)cap << 5) & 0xfffff000; + + if (((ulong)(address + size - 1) >> 36) != 0) + { + return KernelResult.InvalidAddress; + } + + KMemoryPermission perm = (prevCap >> 31) != 0 + ? KMemoryPermission.Read + : KMemoryPermission.ReadAndWrite; + + Result result; + + if ((cap >> 31) != 0) + { + result = memoryManager.MapNormalMemory(address, size, perm); + } + else + { + result = memoryManager.MapIoMemory(address, size, perm); + } + + if (result != Result.Success) + { + return result; + } + } + } + + return Result.Success; + } + + private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager) + { + CapabilityType code = cap.GetCapabilityType(); + + if (code == CapabilityType.Invalid) + { + return KernelResult.InvalidCapability; + } + else if (code == CapabilityType.Padding) + { + return Result.Success; + } + + int codeMask = 1 << (32 - BitOperations.LeadingZeroCount(code.GetFlag() + 1)); + + // Check if the property was already set. + if (((mask0 & codeMask) & 0x1e008) != 0) + { + return KernelResult.InvalidCombination; + } + + mask0 |= codeMask; + + switch (code) + { + case CapabilityType.CorePriority: + { + if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0) + { + return KernelResult.InvalidCapability; + } + + uint lowestCpuCore = (cap >> 16) & 0xff; + uint highestCpuCore = (cap >> 24) & 0xff; + + if (lowestCpuCore > highestCpuCore) + { + return KernelResult.InvalidCombination; + } + + uint highestThreadPrio = (cap >> 4) & 0x3f; + uint 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 CapabilityType.SyscallMask: + { + int slot = ((int)cap >> 29) & 7; + + int svcSlotMask = 1 << slot; + + if ((mask1 & svcSlotMask) != 0) + { + return KernelResult.InvalidCombination; + } + + mask1 |= svcSlotMask; + + uint 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 >= KernelConstants.SupervisorCallCount) + { + return KernelResult.MaximumExceeded; + } + + SvcAccessMask[svcId / 8] |= (byte)(1 << (svcId & 7)); + } + + break; + } + + case CapabilityType.MapIoPage: + { + long address = ((long)cap << 4) & 0xffffff000; + + memoryManager.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite); + + break; + } + + case CapabilityType.MapRegion: + { + // TODO: Implement capabilities for MapRegion + + break; + } + + case CapabilityType.InterruptPair: + { + // TODO: GIC distributor check. + int irq0 = ((int)cap >> 12) & 0x3ff; + int irq1 = ((int)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 CapabilityType.ProgramType: + { + uint applicationType = (cap >> 14); + + if (applicationType > 7) + { + return KernelResult.ReservedValue; + } + + ApplicationType = applicationType; + + break; + } + + case CapabilityType.KernelVersion: + { + // 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 CapabilityType.HandleTable: + { + uint handleTableSize = cap >> 26; + + if (handleTableSize > 0x3ff) + { + return KernelResult.ReservedValue; + } + + HandleTableSize = handleTableSize; + + break; + } + + case CapabilityType.DebugFlags: + { + uint debuggingFlags = cap >> 19; + + if (debuggingFlags > 3) + { + return KernelResult.ReservedValue; + } + + DebuggingFlags &= ~3u; + DebuggingFlags |= debuggingFlags; + + break; + } + + default: return KernelResult.InvalidCapability; + } + + return Result.Success; + } + + private static ulong GetMaskFromMinMax(uint min, uint max) + { + uint range = max - min + 1; + + if (range == 64) + { + return ulong.MaxValue; + } + + ulong mask = (1UL << (int)range) - 1; + + return mask << (int)min; + } + } +}
\ No newline at end of file |