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
This commit is contained in:
parent
b72916fbc1
commit
7870423671
13 changed files with 128 additions and 75 deletions
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
public static Result StartInitialProcess(
|
||||
KernelContext context,
|
||||
ProcessCreationInfo creationInfo,
|
||||
ReadOnlySpan<int> capabilities,
|
||||
ReadOnlySpan<uint> capabilities,
|
||||
int mainThreadPriority,
|
||||
ThreadStart customThreadStart)
|
||||
{
|
||||
|
|
22
Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs
Normal file
22
Ryujinx.HLE/HOS/Kernel/Process/CapabilityExtensions.cs
Normal file
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs
Normal file
19
Ryujinx.HLE/HOS/Kernel/Process/CapabilityType.cs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<int> capabilities,
|
||||
ReadOnlySpan<uint> capabilities,
|
||||
KPageList pageList,
|
||||
KResourceLimit resourceLimit,
|
||||
MemoryRegion memRegion,
|
||||
|
@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
public Result Initialize(
|
||||
ProcessCreationInfo creationInfo,
|
||||
ReadOnlySpan<int> capabilities,
|
||||
ReadOnlySpan<uint> capabilities,
|
||||
KResourceLimit resourceLimit,
|
||||
MemoryRegion memRegion,
|
||||
IProcessContextFactory contextFactory,
|
||||
|
|
|
@ -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<int> capabilities, KPageTableBase memoryManager)
|
||||
public Result InitializeForKernel(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
|
||||
{
|
||||
AllowedCpuCoresMask = 0xf;
|
||||
AllowedThreadPriosMask = ulong.MaxValue;
|
||||
DebuggingFlags &= ~3;
|
||||
DebuggingFlags &= ~3u;
|
||||
KernelReleaseVersion = KProcess.KernelVersionPacked;
|
||||
|
||||
return Parse(capabilities, memoryManager);
|
||||
}
|
||||
|
||||
public Result InitializeForUser(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager)
|
||||
public Result InitializeForUser(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
|
||||
{
|
||||
return Parse(capabilities, memoryManager);
|
||||
}
|
||||
|
||||
private Result Parse(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager)
|
||||
private Result Parse(ReadOnlySpan<uint> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
{
|
||||
[Flags]
|
||||
enum ProcessCreationFlags
|
||||
{
|
||||
Is64Bit = 1 << 0,
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
public Result CreateProcess(
|
||||
out int handle,
|
||||
ProcessCreationInfo info,
|
||||
ReadOnlySpan<int> capabilities,
|
||||
ReadOnlySpan<uint> capabilities,
|
||||
IProcessContextFactory contextFactory,
|
||||
ThreadStart customThreadStart = null)
|
||||
{
|
||||
|
|
|
@ -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<uint>(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<byte, int>(npdm.KernelCapabilityData).ToArray(),
|
||||
MemoryMarshal.Cast<byte, uint>(npdm.KernelCapabilityData),
|
||||
resourceLimit,
|
||||
memoryRegion,
|
||||
processContextFactory);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
Reference in a new issue