From 7870423671cba17c8aad9bb93a67c84bda441366 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Fri, 17 Mar 2023 12:55:19 +0100 Subject: [PATCH] Update syscall capabilites to include SVCs from FW 15.0.0 (#4530) * Add CapabilityType enum * Add SupervisorCallCount * kernel: Add CapabilityExtensions & Change type of capabilities to uint * Remove private setter from Mask arrays * Pass ReadOnlySpan directly & Remove redundant type casts --- Ryujinx.HLE/HOS/Horizon.cs | 4 +- Ryujinx.HLE/HOS/Kernel/KernelConstants.cs | 4 +- Ryujinx.HLE/HOS/Kernel/KernelStatic.cs | 2 +- .../Kernel/Process/CapabilityExtensions.cs | 22 ++++ .../HOS/Kernel/Process/CapabilityType.cs | 19 +++ .../HOS/Kernel/Process/KHandleTable.cs | 6 +- Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 12 +- .../Kernel/Process/KProcessCapabilities.cs | 109 ++++++++++-------- .../Kernel/Process/ProcessCreationFlags.cs | 5 +- .../HOS/Kernel/SupervisorCall/Syscall.cs | 4 +- Ryujinx.HLE/HOS/ProgramLoader.cs | 8 +- Ryujinx.HLE/HOS/Services/ServerBase.cs | 2 +- .../Loaders/Executables/KipExecutable.cs | 6 +- 13 files changed, 128 insertions(+), 75 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 9908cbba..2b77a7c2 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -338,7 +338,7 @@ namespace Ryujinx.HLE.HOS ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0); - int[] defaultCapabilities = new int[] + uint[] defaultCapabilities = new uint[] { 0x030363F7, 0x1FFFFFCF, @@ -552,4 +552,4 @@ namespace Ryujinx.HLE.HOS IsPaused = pause; } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs b/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs index 3817b0aa..28db750c 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelConstants.cs @@ -7,6 +7,8 @@ namespace Ryujinx.HLE.HOS.Kernel public const int InitialKipId = 1; public const int InitialProcessId = 0x51; + public const int SupervisorCallCount = 0xC0; + public const int MemoryBlockAllocatorSize = 0x2710; public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase; @@ -15,4 +17,4 @@ namespace Ryujinx.HLE.HOS.Kernel public const ulong CounterFrequency = 19200000; } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs index 21d7d90c..c66f4b57 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel public static Result StartInitialProcess( KernelContext context, ProcessCreationInfo creationInfo, - ReadOnlySpan capabilities, + ReadOnlySpan capabilities, int mainThreadPriority, ThreadStart customThreadStart) { diff --git a/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs b/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs new file mode 100644 index 00000000..66d56fe3 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs @@ -0,0 +1,22 @@ +using System.Numerics; + +namespace Ryujinx.HLE.HOS.Kernel.Process +{ + static class CapabilityExtensions + { + public static CapabilityType GetCapabilityType(this uint cap) + { + return (CapabilityType)(((cap + 1) & ~cap) - 1); + } + + public static uint GetFlag(this CapabilityType type) + { + return (uint)type + 1; + } + + public static uint GetId(this CapabilityType type) + { + return (uint)BitOperations.TrailingZeroCount(type.GetFlag()); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs b/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs new file mode 100644 index 00000000..51d92316 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.HLE.HOS.Kernel.Process +{ + enum CapabilityType : uint + { + CorePriority = (1u << 3) - 1, + SyscallMask = (1u << 4) - 1, + MapRange = (1u << 6) - 1, + MapIoPage = (1u << 7) - 1, + MapRegion = (1u << 10) - 1, + InterruptPair = (1u << 11) - 1, + ProgramType = (1u << 13) - 1, + KernelVersion = (1u << 14) - 1, + HandleTable = (1u << 15) - 1, + DebugFlags = (1u << 16) - 1, + + Invalid = 0u, + Padding = ~0u + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs b/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs index c15ebef5..50f04e90 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process private int _activeSlotsCount; - private int _size; + private uint _size; private ushort _idCounter; @@ -28,9 +28,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _context = context; } - public Result Initialize(int size) + public Result Initialize(uint size) { - if ((uint)size > 1024) + if (size > 1024) { return KernelResult.OutOfMemory; } diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 8d9cd242..21e89944 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -16,11 +16,11 @@ 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 uint KernelVersionMajor = 10; + public const uint KernelVersionMinor = 4; + public const uint KernelVersionRevision = 0; - public const int KernelVersionPacked = + public const uint KernelVersionPacked = (KernelVersionMajor << 19) | (KernelVersionMinor << 15) | (KernelVersionRevision << 0); @@ -119,7 +119,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public Result InitializeKip( ProcessCreationInfo creationInfo, - ReadOnlySpan capabilities, + ReadOnlySpan capabilities, KPageList pageList, KResourceLimit resourceLimit, MemoryRegion memRegion, @@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public Result Initialize( ProcessCreationInfo creationInfo, - ReadOnlySpan capabilities, + ReadOnlySpan capabilities, KResourceLimit resourceLimit, MemoryRegion memRegion, IProcessContextFactory contextFactory, diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs index ef55a165..c99e3112 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -1,4 +1,3 @@ -using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Horizon.Common; @@ -9,48 +8,49 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { class KProcessCapabilities { - public byte[] SvcAccessMask { get; private set; } - public byte[] IrqAccessMask { get; private set; } + public byte[] SvcAccessMask { get; } + public byte[] IrqAccessMask { get; } public ulong AllowedCpuCoresMask { get; private set; } public ulong 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 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() { - SvcAccessMask = new byte[0x10]; + // length / number of bits of the underlying type + SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / 8]; IrqAccessMask = new byte[0x80]; } - public Result InitializeForKernel(ReadOnlySpan capabilities, KPageTableBase memoryManager) + public Result InitializeForKernel(ReadOnlySpan capabilities, KPageTableBase memoryManager) { AllowedCpuCoresMask = 0xf; AllowedThreadPriosMask = ulong.MaxValue; - DebuggingFlags &= ~3; + DebuggingFlags &= ~3u; KernelReleaseVersion = KProcess.KernelVersionPacked; return Parse(capabilities, memoryManager); } - public Result InitializeForUser(ReadOnlySpan capabilities, KPageTableBase memoryManager) + public Result InitializeForUser(ReadOnlySpan capabilities, KPageTableBase memoryManager) { return Parse(capabilities, memoryManager); } - private Result Parse(ReadOnlySpan capabilities, KPageTableBase memoryManager) + private Result Parse(ReadOnlySpan capabilities, KPageTableBase memoryManager) { int mask0 = 0; int mask1 = 0; for (int index = 0; index < capabilities.Length; index++) { - int cap = capabilities[index]; + uint cap = capabilities[index]; - if (((cap + 1) & ~cap) != 0x40) + if (cap.GetCapabilityType() != CapabilityType.MapRange) { Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager); @@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return KernelResult.InvalidCombination; } - int prevCap = cap; + uint prevCap = cap; cap = capabilities[++index]; @@ -85,8 +85,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return KernelResult.InvalidSize; } - long address = ((long)(uint)prevCap << 5) & 0xffffff000; - long size = ((long)(uint)cap << 5) & 0xfffff000; + long address = ((long)prevCap << 5) & 0xffffff000; + long size = ((long)cap << 5) & 0xfffff000; if (((ulong)(address + size - 1) >> 36) != 0) { @@ -118,20 +118,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return Result.Success; } - private Result ParseCapability(int cap, ref int mask0, ref int mask1, KPageTableBase memoryManager) + private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager) { - int code = (cap + 1) & ~cap; + CapabilityType code = cap.GetCapabilityType(); - if (code == 1) + if (code == CapabilityType.Invalid) { return KernelResult.InvalidCapability; } - else if (code == 0) + else if (code == CapabilityType.Padding) { return Result.Success; } - int codeMask = 1 << (32 - BitOperations.LeadingZeroCount((uint)code + 1)); + int codeMask = 1 << (32 - BitOperations.LeadingZeroCount(code.GetFlag() + 1)); // Check if the property was already set. if (((mask0 & codeMask) & 0x1e008) != 0) @@ -143,23 +143,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Process switch (code) { - case 8: + case CapabilityType.CorePriority: { if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0) { return KernelResult.InvalidCapability; } - int lowestCpuCore = (cap >> 16) & 0xff; - int highestCpuCore = (cap >> 24) & 0xff; + uint lowestCpuCore = (cap >> 16) & 0xff; + uint highestCpuCore = (cap >> 24) & 0xff; if (lowestCpuCore > highestCpuCore) { return KernelResult.InvalidCombination; } - int highestThreadPrio = (cap >> 4) & 0x3f; - int lowestThreadPrio = (cap >> 10) & 0x3f; + uint highestThreadPrio = (cap >> 4) & 0x3f; + uint lowestThreadPrio = (cap >> 10) & 0x3f; if (lowestThreadPrio > highestThreadPrio) { @@ -177,9 +177,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x10: + case CapabilityType.SyscallMask: { - int slot = (cap >> 29) & 7; + int slot = ((int)cap >> 29) & 7; int svcSlotMask = 1 << slot; @@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process mask1 |= svcSlotMask; - int svcMask = (cap >> 5) & 0xffffff; + uint svcMask = (cap >> 5) & 0xffffff; int baseSvc = slot * 24; @@ -203,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process int svcId = baseSvc + index; - if (svcId > 0x7f) + if (svcId >= KernelConstants.SupervisorCallCount) { return KernelResult.MaximumExceeded; } @@ -214,20 +214,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x80: + case CapabilityType.MapIoPage: { - long address = ((long)(uint)cap << 4) & 0xffffff000; + long address = ((long)cap << 4) & 0xffffff000; memoryManager.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite); break; } - case 0x800: + case CapabilityType.MapRegion: + { + // TODO: Implement capabilities for MapRegion + + break; + } + + case CapabilityType.InterruptPair: { // TODO: GIC distributor check. - int irq0 = (cap >> 12) & 0x3ff; - int irq1 = (cap >> 22) & 0x3ff; + int irq0 = ((int)cap >> 12) & 0x3ff; + int irq1 = ((int)cap >> 22) & 0x3ff; if (irq0 != 0x3ff) { @@ -242,11 +249,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x2000: + case CapabilityType.ProgramType: { - int applicationType = cap >> 14; + uint applicationType = (cap >> 14); - if ((uint)applicationType > 7) + if (applicationType > 7) { return KernelResult.ReservedValue; } @@ -256,7 +263,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x4000: + case CapabilityType.KernelVersion: { // Note: This check is bugged on kernel too, we are just replicating the bug here. if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000) @@ -269,11 +276,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x8000: + case CapabilityType.HandleTable: { - int handleTableSize = cap >> 26; + uint handleTableSize = cap >> 26; - if ((uint)handleTableSize > 0x3ff) + if (handleTableSize > 0x3ff) { return KernelResult.ReservedValue; } @@ -283,16 +290,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Process break; } - case 0x10000: + case CapabilityType.DebugFlags: { - int debuggingFlags = cap >> 19; + uint debuggingFlags = cap >> 19; - if ((uint)debuggingFlags > 3) + if (debuggingFlags > 3) { return KernelResult.ReservedValue; } - DebuggingFlags &= ~3; + DebuggingFlags &= ~3u; DebuggingFlags |= debuggingFlags; break; @@ -304,18 +311,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return Result.Success; } - private static ulong GetMaskFromMinMax(int min, int max) + private static ulong GetMaskFromMinMax(uint min, uint max) { - int range = max - min + 1; + uint range = max - min + 1; if (range == 64) { return ulong.MaxValue; } - ulong mask = (1UL << range) - 1; + ulong mask = (1UL << (int)range) - 1; - return mask << min; + return mask << (int)min; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs index a34481e5..a79978ac 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs @@ -1,5 +1,8 @@ -namespace Ryujinx.HLE.HOS.Kernel.Process +using System; + +namespace Ryujinx.HLE.HOS.Kernel.Process { + [Flags] enum ProcessCreationFlags { Is64Bit = 1 << 0, diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index e23274eb..eef78e18 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall public Result CreateProcess( out int handle, ProcessCreationInfo info, - ReadOnlySpan capabilities, + ReadOnlySpan capabilities, IProcessContextFactory contextFactory, ThreadStart customThreadStart = null) { @@ -3002,4 +3002,4 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return (address & 3) != 0; } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 158ab701..4ebcb7e7 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL; - ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset; + ulong codeAddress = codeBaseAddress + kip.TextOffset; ProcessCreationFlags flags = 0; @@ -231,13 +231,13 @@ namespace Ryujinx.HLE.HOS nsoSize = BitUtils.AlignUp(nsoSize, KPageTableBase.PageSize); - nsoBase[index] = codeStart + (ulong)codeSize; + nsoBase[index] = codeStart + codeSize; codeSize += nsoSize; if (arguments != null && argsSize == 0) { - argsStart = (ulong)codeSize; + argsStart = codeSize; argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize); @@ -318,7 +318,7 @@ namespace Ryujinx.HLE.HOS result = process.Initialize( creationInfo, - MemoryMarshal.Cast(npdm.KernelCapabilityData).ToArray(), + MemoryMarshal.Cast(npdm.KernelCapabilityData), resourceLimit, memoryRegion, processContextFactory); diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index 50f6c99e..d4382a64 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services // not large enough. private const int PointerBufferSize = 0x8000; - private readonly static int[] DefaultCapabilities = new int[] + private readonly static uint[] DefaultCapabilities = new uint[] { 0x030363F7, 0x1FFFFFCF, diff --git a/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs b/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs index 2073a7e2..ad2b681c 100644 --- a/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs +++ b/Ryujinx.HLE/Loaders/Executables/KipExecutable.cs @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.Loaders.Executables public uint DataSize { get; } public uint BssSize { get; } - public int[] Capabilities { get; } + public uint[] Capabilities { get; } public bool UsesSecureMemory { get; } public bool Is64BitAddressSpace { get; } public bool Is64Bit { get; } @@ -57,11 +57,11 @@ namespace Ryujinx.HLE.Loaders.Executables Version = reader.Version; Name = reader.Name.ToString(); - Capabilities = new int[32]; + Capabilities = new uint[32]; for (int index = 0; index < Capabilities.Length; index++) { - Capabilities[index] = (int)reader.Capabilities[index]; + Capabilities[index] = reader.Capabilities[index]; } reader.GetSegmentSize(KipReader.SegmentType.Data, out int uncompressedSize).ThrowIfFailure();