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:
TSRBerry 2023-03-17 12:55:19 +01:00 committed by GitHub
parent b72916fbc1
commit 7870423671
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 128 additions and 75 deletions

View file

@ -338,7 +338,7 @@ namespace Ryujinx.HLE.HOS
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0); ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
int[] defaultCapabilities = new int[] uint[] defaultCapabilities = new uint[]
{ {
0x030363F7, 0x030363F7,
0x1FFFFFCF, 0x1FFFFFCF,
@ -552,4 +552,4 @@ namespace Ryujinx.HLE.HOS
IsPaused = pause; IsPaused = pause;
} }
} }
} }

View file

@ -7,6 +7,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public const int InitialKipId = 1; public const int InitialKipId = 1;
public const int InitialProcessId = 0x51; public const int InitialProcessId = 0x51;
public const int SupervisorCallCount = 0xC0;
public const int MemoryBlockAllocatorSize = 0x2710; public const int MemoryBlockAllocatorSize = 0x2710;
public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase; public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
@ -15,4 +17,4 @@ namespace Ryujinx.HLE.HOS.Kernel
public const ulong CounterFrequency = 19200000; public const ulong CounterFrequency = 19200000;
} }
} }

View file

@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel
public static Result StartInitialProcess( public static Result StartInitialProcess(
KernelContext context, KernelContext context,
ProcessCreationInfo creationInfo, ProcessCreationInfo creationInfo,
ReadOnlySpan<int> capabilities, ReadOnlySpan<uint> capabilities,
int mainThreadPriority, int mainThreadPriority,
ThreadStart customThreadStart) ThreadStart customThreadStart)
{ {

View 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());
}
}
}

View 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
}
}

View file

@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private int _activeSlotsCount; private int _activeSlotsCount;
private int _size; private uint _size;
private ushort _idCounter; private ushort _idCounter;
@ -28,9 +28,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
_context = context; _context = context;
} }
public Result Initialize(int size) public Result Initialize(uint size)
{ {
if ((uint)size > 1024) if (size > 1024)
{ {
return KernelResult.OutOfMemory; return KernelResult.OutOfMemory;
} }

View file

@ -16,11 +16,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
class KProcess : KSynchronizationObject class KProcess : KSynchronizationObject
{ {
public const int KernelVersionMajor = 10; public const uint KernelVersionMajor = 10;
public const int KernelVersionMinor = 4; public const uint KernelVersionMinor = 4;
public const int KernelVersionRevision = 0; public const uint KernelVersionRevision = 0;
public const int KernelVersionPacked = public const uint KernelVersionPacked =
(KernelVersionMajor << 19) | (KernelVersionMajor << 19) |
(KernelVersionMinor << 15) | (KernelVersionMinor << 15) |
(KernelVersionRevision << 0); (KernelVersionRevision << 0);
@ -119,7 +119,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public Result InitializeKip( public Result InitializeKip(
ProcessCreationInfo creationInfo, ProcessCreationInfo creationInfo,
ReadOnlySpan<int> capabilities, ReadOnlySpan<uint> capabilities,
KPageList pageList, KPageList pageList,
KResourceLimit resourceLimit, KResourceLimit resourceLimit,
MemoryRegion memRegion, MemoryRegion memRegion,
@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public Result Initialize( public Result Initialize(
ProcessCreationInfo creationInfo, ProcessCreationInfo creationInfo,
ReadOnlySpan<int> capabilities, ReadOnlySpan<uint> capabilities,
KResourceLimit resourceLimit, KResourceLimit resourceLimit,
MemoryRegion memRegion, MemoryRegion memRegion,
IProcessContextFactory contextFactory, IProcessContextFactory contextFactory,

View file

@ -1,4 +1,3 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
@ -9,48 +8,49 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
class KProcessCapabilities class KProcessCapabilities
{ {
public byte[] SvcAccessMask { get; private set; } public byte[] SvcAccessMask { get; }
public byte[] IrqAccessMask { get; private set; } public byte[] IrqAccessMask { get; }
public ulong AllowedCpuCoresMask { get; private set; } public ulong AllowedCpuCoresMask { get; private set; }
public ulong AllowedThreadPriosMask { get; private set; } public ulong AllowedThreadPriosMask { get; private set; }
public int DebuggingFlags { get; private set; } public uint DebuggingFlags { get; private set; }
public int HandleTableSize { get; private set; } public uint HandleTableSize { get; private set; }
public int KernelReleaseVersion { get; private set; } public uint KernelReleaseVersion { get; private set; }
public int ApplicationType { get; private set; } public uint ApplicationType { get; private set; }
public KProcessCapabilities() public KProcessCapabilities()
{ {
SvcAccessMask = new byte[0x10]; // length / number of bits of the underlying type
SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / 8];
IrqAccessMask = new byte[0x80]; IrqAccessMask = new byte[0x80];
} }
public Result InitializeForKernel(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager) public Result InitializeForKernel(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
{ {
AllowedCpuCoresMask = 0xf; AllowedCpuCoresMask = 0xf;
AllowedThreadPriosMask = ulong.MaxValue; AllowedThreadPriosMask = ulong.MaxValue;
DebuggingFlags &= ~3; DebuggingFlags &= ~3u;
KernelReleaseVersion = KProcess.KernelVersionPacked; KernelReleaseVersion = KProcess.KernelVersionPacked;
return Parse(capabilities, memoryManager); return Parse(capabilities, memoryManager);
} }
public Result InitializeForUser(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager) public Result InitializeForUser(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
{ {
return Parse(capabilities, 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 mask0 = 0;
int mask1 = 0; int mask1 = 0;
for (int index = 0; index < capabilities.Length; index++) 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); Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager);
@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return KernelResult.InvalidCombination; return KernelResult.InvalidCombination;
} }
int prevCap = cap; uint prevCap = cap;
cap = capabilities[++index]; cap = capabilities[++index];
@ -85,8 +85,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return KernelResult.InvalidSize; return KernelResult.InvalidSize;
} }
long address = ((long)(uint)prevCap << 5) & 0xffffff000; long address = ((long)prevCap << 5) & 0xffffff000;
long size = ((long)(uint)cap << 5) & 0xfffff000; long size = ((long)cap << 5) & 0xfffff000;
if (((ulong)(address + size - 1) >> 36) != 0) if (((ulong)(address + size - 1) >> 36) != 0)
{ {
@ -118,20 +118,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return Result.Success; 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; return KernelResult.InvalidCapability;
} }
else if (code == 0) else if (code == CapabilityType.Padding)
{ {
return Result.Success; 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. // Check if the property was already set.
if (((mask0 & codeMask) & 0x1e008) != 0) if (((mask0 & codeMask) & 0x1e008) != 0)
@ -143,23 +143,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
switch (code) switch (code)
{ {
case 8: case CapabilityType.CorePriority:
{ {
if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0) if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
{ {
return KernelResult.InvalidCapability; return KernelResult.InvalidCapability;
} }
int lowestCpuCore = (cap >> 16) & 0xff; uint lowestCpuCore = (cap >> 16) & 0xff;
int highestCpuCore = (cap >> 24) & 0xff; uint highestCpuCore = (cap >> 24) & 0xff;
if (lowestCpuCore > highestCpuCore) if (lowestCpuCore > highestCpuCore)
{ {
return KernelResult.InvalidCombination; return KernelResult.InvalidCombination;
} }
int highestThreadPrio = (cap >> 4) & 0x3f; uint highestThreadPrio = (cap >> 4) & 0x3f;
int lowestThreadPrio = (cap >> 10) & 0x3f; uint lowestThreadPrio = (cap >> 10) & 0x3f;
if (lowestThreadPrio > highestThreadPrio) if (lowestThreadPrio > highestThreadPrio)
{ {
@ -177,9 +177,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break; break;
} }
case 0x10: case CapabilityType.SyscallMask:
{ {
int slot = (cap >> 29) & 7; int slot = ((int)cap >> 29) & 7;
int svcSlotMask = 1 << slot; int svcSlotMask = 1 << slot;
@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
mask1 |= svcSlotMask; mask1 |= svcSlotMask;
int svcMask = (cap >> 5) & 0xffffff; uint svcMask = (cap >> 5) & 0xffffff;
int baseSvc = slot * 24; int baseSvc = slot * 24;
@ -203,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
int svcId = baseSvc + index; int svcId = baseSvc + index;
if (svcId > 0x7f) if (svcId >= KernelConstants.SupervisorCallCount)
{ {
return KernelResult.MaximumExceeded; return KernelResult.MaximumExceeded;
} }
@ -214,20 +214,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break; 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); memoryManager.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite);
break; break;
} }
case 0x800: case CapabilityType.MapRegion:
{
// TODO: Implement capabilities for MapRegion
break;
}
case CapabilityType.InterruptPair:
{ {
// TODO: GIC distributor check. // TODO: GIC distributor check.
int irq0 = (cap >> 12) & 0x3ff; int irq0 = ((int)cap >> 12) & 0x3ff;
int irq1 = (cap >> 22) & 0x3ff; int irq1 = ((int)cap >> 22) & 0x3ff;
if (irq0 != 0x3ff) if (irq0 != 0x3ff)
{ {
@ -242,11 +249,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break; break;
} }
case 0x2000: case CapabilityType.ProgramType:
{ {
int applicationType = cap >> 14; uint applicationType = (cap >> 14);
if ((uint)applicationType > 7) if (applicationType > 7)
{ {
return KernelResult.ReservedValue; return KernelResult.ReservedValue;
} }
@ -256,7 +263,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break; break;
} }
case 0x4000: case CapabilityType.KernelVersion:
{ {
// Note: This check is bugged on kernel too, we are just replicating the bug here. // Note: This check is bugged on kernel too, we are just replicating the bug here.
if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000) if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000)
@ -269,11 +276,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break; break;
} }
case 0x8000: case CapabilityType.HandleTable:
{ {
int handleTableSize = cap >> 26; uint handleTableSize = cap >> 26;
if ((uint)handleTableSize > 0x3ff) if (handleTableSize > 0x3ff)
{ {
return KernelResult.ReservedValue; return KernelResult.ReservedValue;
} }
@ -283,16 +290,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break; break;
} }
case 0x10000: case CapabilityType.DebugFlags:
{ {
int debuggingFlags = cap >> 19; uint debuggingFlags = cap >> 19;
if ((uint)debuggingFlags > 3) if (debuggingFlags > 3)
{ {
return KernelResult.ReservedValue; return KernelResult.ReservedValue;
} }
DebuggingFlags &= ~3; DebuggingFlags &= ~3u;
DebuggingFlags |= debuggingFlags; DebuggingFlags |= debuggingFlags;
break; break;
@ -304,18 +311,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return Result.Success; 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) if (range == 64)
{ {
return ulong.MaxValue; return ulong.MaxValue;
} }
ulong mask = (1UL << range) - 1; ulong mask = (1UL << (int)range) - 1;
return mask << min; return mask << (int)min;
} }
} }
} }

View file

@ -1,5 +1,8 @@
namespace Ryujinx.HLE.HOS.Kernel.Process using System;
namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
[Flags]
enum ProcessCreationFlags enum ProcessCreationFlags
{ {
Is64Bit = 1 << 0, Is64Bit = 1 << 0,

View file

@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public Result CreateProcess( public Result CreateProcess(
out int handle, out int handle,
ProcessCreationInfo info, ProcessCreationInfo info,
ReadOnlySpan<int> capabilities, ReadOnlySpan<uint> capabilities,
IProcessContextFactory contextFactory, IProcessContextFactory contextFactory,
ThreadStart customThreadStart = null) ThreadStart customThreadStart = null)
{ {
@ -3002,4 +3002,4 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return (address & 3) != 0; return (address & 3) != 0;
} }
} }
} }

View file

@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS
ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL; ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL;
ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset; ulong codeAddress = codeBaseAddress + kip.TextOffset;
ProcessCreationFlags flags = 0; ProcessCreationFlags flags = 0;
@ -231,13 +231,13 @@ namespace Ryujinx.HLE.HOS
nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize); nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize);
nsoBase[index] = codeStart + (ulong)codeSize; nsoBase[index] = codeStart + codeSize;
codeSize += nsoSize; codeSize += nsoSize;
if (arguments != null && argsSize == 0) if (arguments != null && argsSize == 0)
{ {
argsStart = (ulong)codeSize; argsStart = codeSize;
argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize); argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize);
@ -318,7 +318,7 @@ namespace Ryujinx.HLE.HOS
result = process.Initialize( result = process.Initialize(
creationInfo, creationInfo,
MemoryMarshal.Cast<byte, int>(npdm.KernelCapabilityData).ToArray(), MemoryMarshal.Cast<byte, uint>(npdm.KernelCapabilityData),
resourceLimit, resourceLimit,
memoryRegion, memoryRegion,
processContextFactory); processContextFactory);

View file

@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services
// not large enough. // not large enough.
private const int PointerBufferSize = 0x8000; private const int PointerBufferSize = 0x8000;
private readonly static int[] DefaultCapabilities = new int[] private readonly static uint[] DefaultCapabilities = new uint[]
{ {
0x030363F7, 0x030363F7,
0x1FFFFFCF, 0x1FFFFFCF,

View file

@ -22,7 +22,7 @@ namespace Ryujinx.HLE.Loaders.Executables
public uint DataSize { get; } public uint DataSize { get; }
public uint BssSize { get; } public uint BssSize { get; }
public int[] Capabilities { get; } public uint[] Capabilities { get; }
public bool UsesSecureMemory { get; } public bool UsesSecureMemory { get; }
public bool Is64BitAddressSpace { get; } public bool Is64BitAddressSpace { get; }
public bool Is64Bit { get; } public bool Is64Bit { get; }
@ -57,11 +57,11 @@ namespace Ryujinx.HLE.Loaders.Executables
Version = reader.Version; Version = reader.Version;
Name = reader.Name.ToString(); Name = reader.Name.ToString();
Capabilities = new int[32]; Capabilities = new uint[32];
for (int index = 0; index < Capabilities.Length; index++) 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(); reader.GetSegmentSize(KipReader.SegmentType.Data, out int uncompressedSize).ThrowIfFailure();