forked from Mirror/Ryujinx
Improve kernel IPC implementation (#550)
* Implement some IPC related kernel SVCs properly * Fix BLZ decompression when the segment also has a uncompressed chunck * Set default cpu core on process start from ProgramLoader, remove debug message * Load process capabilities properly on KIPs * Fix a copy/paste error in UnmapPhysicalMemory64 * Implement smarter switching between old and new IPC system to support the old HLE services implementation without the manual switch * Implement RegisterService on sm and AcceptSession (partial) * Misc fixes and improvements on new IPC methods * Move IPC related SVCs into a separate file, and logging on RegisterService (sm) * Some small fixes related to receive list buffers and error cases * Load NSOs using the correct pool partition * Fix corner case on GetMaskFromMinMax where range is 64, doesn't happen in pratice however * Fix send static buffer copy * Session release, implement closing requests on client disconnect * Implement ConnectToPort SVC * KLightSession init
This commit is contained in:
parent
3731d0ce84
commit
22bacc6188
51 changed files with 4310 additions and 840 deletions
|
@ -113,6 +113,63 @@ namespace Ryujinx.HLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Set(ulong address, byte value, ulong size)
|
||||||
|
{
|
||||||
|
if (address + size < address)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address + size > RamSize)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong size8 = size & ~7UL;
|
||||||
|
|
||||||
|
ulong valueRep = (ulong)value * 0x0101010101010101;
|
||||||
|
|
||||||
|
for (ulong offs = 0; offs < size8; offs += 8)
|
||||||
|
{
|
||||||
|
WriteUInt64((long)(address + offs), valueRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ulong offs = size8; offs < (size - size8); offs++)
|
||||||
|
{
|
||||||
|
WriteByte((long)(address + offs), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Copy(ulong dst, ulong src, ulong size)
|
||||||
|
{
|
||||||
|
if (dst + size < dst || src + size < src)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst + size > RamSize)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(dst));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src + size > RamSize)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(src));
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong size8 = size & ~7UL;
|
||||||
|
|
||||||
|
for (ulong offs = 0; offs < size8; offs += 8)
|
||||||
|
{
|
||||||
|
WriteUInt64((long)(dst + offs), ReadUInt64((long)(src + offs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ulong offs = size8; offs < (size - size8); offs++)
|
||||||
|
{
|
||||||
|
WriteByte((long)(dst + offs), ReadByte((long)(src + offs)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
|
|
@ -7,6 +7,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Sm;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.Loaders.Npdm;
|
using Ryujinx.HLE.Loaders.Npdm;
|
||||||
|
@ -157,8 +158,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
hidPageList .AddRange(hidPa, HidSize / KMemoryManager.PageSize);
|
hidPageList .AddRange(hidPa, HidSize / KMemoryManager.PageSize);
|
||||||
fontPageList.AddRange(fontPa, FontSize / KMemoryManager.PageSize);
|
fontPageList.AddRange(fontPa, FontSize / KMemoryManager.PageSize);
|
||||||
|
|
||||||
HidSharedMem = new KSharedMemory(hidPageList, 0, 0, MemoryPermission.Read);
|
HidSharedMem = new KSharedMemory(this, hidPageList, 0, 0, MemoryPermission.Read);
|
||||||
FontSharedMem = new KSharedMemory(fontPageList, 0, 0, MemoryPermission.Read);
|
FontSharedMem = new KSharedMemory(this, fontPageList, 0, 0, MemoryPermission.Read);
|
||||||
|
|
||||||
AppletState = new AppletStateMgr(this);
|
AppletState = new AppletStateMgr(this);
|
||||||
|
|
||||||
|
@ -166,6 +167,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
Font = new SharedFontManager(device, (long)(fontPa - DramMemoryMap.DramBase));
|
Font = new SharedFontManager(device, (long)(fontPa - DramMemoryMap.DramBase));
|
||||||
|
|
||||||
|
IUserInterface.InitializePort(this);
|
||||||
|
|
||||||
VsyncEvent = new KEvent(this);
|
VsyncEvent = new KEvent(this);
|
||||||
|
|
||||||
LoadKeySet();
|
LoadKeySet();
|
||||||
|
@ -259,6 +262,14 @@ namespace Ryujinx.HLE.HOS
|
||||||
LoadNca(mainNca, controlNca);
|
LoadNca(mainNca, controlNca);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LoadKip(string kipFile)
|
||||||
|
{
|
||||||
|
using (FileStream fs = new FileStream(kipFile, FileMode.Open))
|
||||||
|
{
|
||||||
|
ProgramLoader.LoadKernelInitalProcess(this, new KernelInitialProcess(fs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private (Nca Main, Nca Control) GetXciGameData(Xci xci)
|
private (Nca Main, Nca Control) GetXciGameData(Xci xci)
|
||||||
{
|
{
|
||||||
if (xci.SecurePartition == null)
|
if (xci.SecurePartition == null)
|
||||||
|
|
|
@ -10,12 +10,12 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
static class IpcHandler
|
static class IpcHandler
|
||||||
{
|
{
|
||||||
public static KernelResult IpcCall(
|
public static KernelResult IpcCall(
|
||||||
Switch device,
|
Switch device,
|
||||||
KProcess process,
|
KProcess process,
|
||||||
MemoryManager memory,
|
MemoryManager memory,
|
||||||
KSession session,
|
KClientSession session,
|
||||||
IpcMessage request,
|
IpcMessage request,
|
||||||
long cmdPtr)
|
long cmdPtr)
|
||||||
{
|
{
|
||||||
IpcMessage response = new IpcMessage();
|
IpcMessage response = new IpcMessage();
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
class KAutoObject
|
class KAutoObject
|
||||||
{
|
{
|
||||||
protected Horizon System;
|
protected Horizon System;
|
||||||
|
|
||||||
|
private int _referenceCount;
|
||||||
|
|
||||||
public KAutoObject(Horizon system)
|
public KAutoObject(Horizon system)
|
||||||
{
|
{
|
||||||
System = system;
|
System = system;
|
||||||
|
|
||||||
|
_referenceCount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual KernelResult SetName(string name)
|
public virtual KernelResult SetName(string name)
|
||||||
|
@ -38,5 +44,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void IncrementReferenceCount()
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _referenceCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DecrementReferenceCount()
|
||||||
|
{
|
||||||
|
if (Interlocked.Decrement(ref _referenceCount) == 0)
|
||||||
|
{
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Destroy() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
class KResourceLimit
|
class KResourceLimit : KAutoObject
|
||||||
{
|
{
|
||||||
private const int Time10SecondsMs = 10000;
|
private const int Time10SecondsMs = 10000;
|
||||||
|
|
||||||
|
@ -18,9 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
|
|
||||||
private int _waitingThreadsCount;
|
private int _waitingThreadsCount;
|
||||||
|
|
||||||
private Horizon _system;
|
public KResourceLimit(Horizon system) : base(system)
|
||||||
|
|
||||||
public KResourceLimit(Horizon system)
|
|
||||||
{
|
{
|
||||||
_current = new long[(int)LimitableResource.Count];
|
_current = new long[(int)LimitableResource.Count];
|
||||||
_limit = new long[(int)LimitableResource.Count];
|
_limit = new long[(int)LimitableResource.Count];
|
||||||
|
@ -29,8 +27,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
_lockObj = new object();
|
_lockObj = new object();
|
||||||
|
|
||||||
_waitingThreads = new LinkedList<KThread>();
|
_waitingThreads = new LinkedList<KThread>();
|
||||||
|
|
||||||
_system = system;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Reserve(LimitableResource resource, ulong amount)
|
public bool Reserve(LimitableResource resource, ulong amount)
|
||||||
|
@ -61,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
_waitingThreadsCount++;
|
_waitingThreadsCount++;
|
||||||
|
|
||||||
KConditionVariable.Wait(_system, _waitingThreads, _lockObj, timeout);
|
KConditionVariable.Wait(System, _waitingThreads, _lockObj, timeout);
|
||||||
|
|
||||||
_waitingThreadsCount--;
|
_waitingThreadsCount--;
|
||||||
|
|
||||||
|
@ -94,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
Release(resource, amount, amount);
|
Release(resource, amount, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Release(LimitableResource resource, long usedAmount, long availableAmount)
|
public void Release(LimitableResource resource, long usedAmount, long availableAmount)
|
||||||
{
|
{
|
||||||
int index = GetIndex(resource);
|
int index = GetIndex(resource);
|
||||||
|
|
||||||
|
@ -105,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
|
|
||||||
if (_waitingThreadsCount > 0)
|
if (_waitingThreadsCount > 0)
|
||||||
{
|
{
|
||||||
KConditionVariable.NotifyAll(_system, _waitingThreads);
|
KConditionVariable.NotifyAll(System, _waitingThreads);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
class KSynchronizationObject : KAutoObject
|
class KSynchronizationObject : KAutoObject
|
||||||
{
|
{
|
||||||
public LinkedList<KThread> WaitingThreads;
|
public LinkedList<KThread> WaitingThreads { get; }
|
||||||
|
|
||||||
public KSynchronizationObject(Horizon system) : base(system)
|
public KSynchronizationObject(Horizon system) : base(system)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,31 +2,36 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
enum KernelResult
|
enum KernelResult
|
||||||
{
|
{
|
||||||
Success = 0,
|
Success = 0,
|
||||||
InvalidCapability = 0x1c01,
|
SessionCountExceeded = 0xe01,
|
||||||
ThreadNotStarted = 0x7201,
|
InvalidCapability = 0x1c01,
|
||||||
ThreadTerminating = 0x7601,
|
ThreadNotStarted = 0x7201,
|
||||||
InvalidSize = 0xca01,
|
ThreadTerminating = 0x7601,
|
||||||
InvalidAddress = 0xcc01,
|
InvalidSize = 0xca01,
|
||||||
OutOfResource = 0xce01,
|
InvalidAddress = 0xcc01,
|
||||||
OutOfMemory = 0xd001,
|
OutOfResource = 0xce01,
|
||||||
HandleTableFull = 0xd201,
|
OutOfMemory = 0xd001,
|
||||||
InvalidMemState = 0xd401,
|
HandleTableFull = 0xd201,
|
||||||
InvalidPermission = 0xd801,
|
InvalidMemState = 0xd401,
|
||||||
InvalidMemRange = 0xdc01,
|
InvalidPermission = 0xd801,
|
||||||
InvalidPriority = 0xe001,
|
InvalidMemRange = 0xdc01,
|
||||||
InvalidCpuCore = 0xe201,
|
InvalidPriority = 0xe001,
|
||||||
InvalidHandle = 0xe401,
|
InvalidCpuCore = 0xe201,
|
||||||
UserCopyFailed = 0xe601,
|
InvalidHandle = 0xe401,
|
||||||
InvalidCombination = 0xe801,
|
UserCopyFailed = 0xe601,
|
||||||
TimedOut = 0xea01,
|
InvalidCombination = 0xe801,
|
||||||
Cancelled = 0xec01,
|
TimedOut = 0xea01,
|
||||||
MaximumExceeded = 0xee01,
|
Cancelled = 0xec01,
|
||||||
InvalidEnumValue = 0xf001,
|
MaximumExceeded = 0xee01,
|
||||||
NotFound = 0xf201,
|
InvalidEnumValue = 0xf001,
|
||||||
InvalidThread = 0xf401,
|
NotFound = 0xf201,
|
||||||
InvalidState = 0xfa01,
|
InvalidThread = 0xf401,
|
||||||
ReservedValue = 0xfc01,
|
PortRemoteClosed = 0xf601,
|
||||||
ResLimitExceeded = 0x10801
|
InvalidState = 0xfa01,
|
||||||
|
ReservedValue = 0xfc01,
|
||||||
|
PortClosed = 0x10601,
|
||||||
|
ResLimitExceeded = 0x10801,
|
||||||
|
OutOfVaSpace = 0x20601,
|
||||||
|
CmdBufferTooSmall = 0x20801
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -22,6 +22,26 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool UserToKernelInt32Array(Horizon system, ulong address, int[] values)
|
||||||
|
{
|
||||||
|
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
for (int index = 0; index < values.Length; index++, address += 4)
|
||||||
|
{
|
||||||
|
if (currentProcess.CpuMemory.IsMapped((long)address) &&
|
||||||
|
currentProcess.CpuMemory.IsMapped((long)address + 3))
|
||||||
|
{
|
||||||
|
values[index]= currentProcess.CpuMemory.ReadInt32((long)address);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static bool UserToKernelString(Horizon system, ulong address, int size, out string value)
|
public static bool UserToKernelString(Horizon system, ulong address, int size, out string value)
|
||||||
{
|
{
|
||||||
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
||||||
|
|
10
Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs
Normal file
10
Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
{
|
||||||
|
enum ChannelState
|
||||||
|
{
|
||||||
|
NotInitialized,
|
||||||
|
Open,
|
||||||
|
ClientDisconnected,
|
||||||
|
ServerDisconnected
|
||||||
|
}
|
||||||
|
}
|
20
Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs
Normal file
20
Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
{
|
||||||
|
class KBufferDescriptor
|
||||||
|
{
|
||||||
|
public ulong ClientAddress { get; }
|
||||||
|
public ulong ServerAddress { get; }
|
||||||
|
public ulong Size { get; }
|
||||||
|
public MemoryState State { get; }
|
||||||
|
|
||||||
|
public KBufferDescriptor(ulong src, ulong dst, ulong size, MemoryState state)
|
||||||
|
{
|
||||||
|
ClientAddress = src;
|
||||||
|
ServerAddress = dst;
|
||||||
|
Size = size;
|
||||||
|
State = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
216
Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
Normal file
216
Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
{
|
||||||
|
class KBufferDescriptorTable
|
||||||
|
{
|
||||||
|
private const int MaxInternalBuffersCount = 8;
|
||||||
|
|
||||||
|
private List<KBufferDescriptor> _sendBufferDescriptors;
|
||||||
|
private List<KBufferDescriptor> _receiveBufferDescriptors;
|
||||||
|
private List<KBufferDescriptor> _exchangeBufferDescriptors;
|
||||||
|
|
||||||
|
public KBufferDescriptorTable()
|
||||||
|
{
|
||||||
|
_sendBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
|
||||||
|
_receiveBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
|
||||||
|
_exchangeBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult AddSendBuffer(ulong src, ulong dst, ulong size, MemoryState state)
|
||||||
|
{
|
||||||
|
return Add(_sendBufferDescriptors, src, dst, size, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult AddReceiveBuffer(ulong src, ulong dst, ulong size, MemoryState state)
|
||||||
|
{
|
||||||
|
return Add(_receiveBufferDescriptors, src, dst, size, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult AddExchangeBuffer(ulong src, ulong dst, ulong size, MemoryState state)
|
||||||
|
{
|
||||||
|
return Add(_exchangeBufferDescriptors, src, dst, size, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult Add(List<KBufferDescriptor> list, ulong src, ulong dst, ulong size, MemoryState state)
|
||||||
|
{
|
||||||
|
if (list.Count < MaxInternalBuffersCount)
|
||||||
|
{
|
||||||
|
list.Add(new KBufferDescriptor(src, dst, size, state));
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KernelResult.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult CopyBuffersToClient(KMemoryManager memoryManager)
|
||||||
|
{
|
||||||
|
KernelResult result = CopyToClient(memoryManager, _receiveBufferDescriptors);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CopyToClient(memoryManager, _exchangeBufferDescriptors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult CopyToClient(KMemoryManager memoryManager, List<KBufferDescriptor> list)
|
||||||
|
{
|
||||||
|
foreach (KBufferDescriptor desc in list)
|
||||||
|
{
|
||||||
|
MemoryState stateMask;
|
||||||
|
|
||||||
|
switch (desc.State)
|
||||||
|
{
|
||||||
|
case MemoryState.IpcBuffer0: stateMask = MemoryState.IpcSendAllowedType0; break;
|
||||||
|
case MemoryState.IpcBuffer1: stateMask = MemoryState.IpcSendAllowedType1; break;
|
||||||
|
case MemoryState.IpcBuffer3: stateMask = MemoryState.IpcSendAllowedType3; break;
|
||||||
|
|
||||||
|
default: return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryAttribute attributeMask = MemoryAttribute.Borrowed | MemoryAttribute.Uncached;
|
||||||
|
|
||||||
|
if (desc.State == MemoryState.IpcBuffer0)
|
||||||
|
{
|
||||||
|
attributeMask |= MemoryAttribute.DeviceMapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong clientAddrTruncated = BitUtils.AlignDown(desc.ClientAddress, KMemoryManager.PageSize);
|
||||||
|
ulong clientAddrRounded = BitUtils.AlignUp (desc.ClientAddress, KMemoryManager.PageSize);
|
||||||
|
|
||||||
|
//Check if address is not aligned, in this case we need to perform 2 copies.
|
||||||
|
if (clientAddrTruncated != clientAddrRounded)
|
||||||
|
{
|
||||||
|
ulong copySize = clientAddrRounded - desc.ClientAddress;
|
||||||
|
|
||||||
|
if (copySize > desc.Size)
|
||||||
|
{
|
||||||
|
copySize = desc.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelResult result = memoryManager.CopyDataFromCurrentProcess(
|
||||||
|
desc.ClientAddress,
|
||||||
|
copySize,
|
||||||
|
stateMask,
|
||||||
|
stateMask,
|
||||||
|
MemoryPermission.ReadAndWrite,
|
||||||
|
attributeMask,
|
||||||
|
MemoryAttribute.None,
|
||||||
|
desc.ServerAddress);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong clientEndAddr = desc.ClientAddress + desc.Size;
|
||||||
|
ulong serverEndAddr = desc.ServerAddress + desc.Size;
|
||||||
|
|
||||||
|
ulong clientEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
|
||||||
|
ulong clientEndAddrRounded = BitUtils.AlignUp (clientEndAddr, KMemoryManager.PageSize);
|
||||||
|
ulong serverEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
|
||||||
|
|
||||||
|
if (clientEndAddrTruncated < clientAddrRounded)
|
||||||
|
{
|
||||||
|
KernelResult result = memoryManager.CopyDataToCurrentProcess(
|
||||||
|
clientEndAddrTruncated,
|
||||||
|
clientEndAddr - clientEndAddrTruncated,
|
||||||
|
serverEndAddrTruncated,
|
||||||
|
stateMask,
|
||||||
|
stateMask,
|
||||||
|
MemoryPermission.ReadAndWrite,
|
||||||
|
attributeMask,
|
||||||
|
MemoryAttribute.None);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult UnmapServerBuffers(KMemoryManager memoryManager)
|
||||||
|
{
|
||||||
|
KernelResult result = UnmapServer(memoryManager, _sendBufferDescriptors);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = UnmapServer(memoryManager, _receiveBufferDescriptors);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnmapServer(memoryManager, _exchangeBufferDescriptors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult UnmapServer(KMemoryManager memoryManager, List<KBufferDescriptor> list)
|
||||||
|
{
|
||||||
|
foreach (KBufferDescriptor descriptor in list)
|
||||||
|
{
|
||||||
|
KernelResult result = memoryManager.UnmapNoAttributeIfStateEquals(
|
||||||
|
descriptor.ServerAddress,
|
||||||
|
descriptor.Size,
|
||||||
|
descriptor.State);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult RestoreClientBuffers(KMemoryManager memoryManager)
|
||||||
|
{
|
||||||
|
KernelResult result = RestoreClient(memoryManager, _sendBufferDescriptors);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = RestoreClient(memoryManager, _receiveBufferDescriptors);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RestoreClient(memoryManager, _exchangeBufferDescriptors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult RestoreClient(KMemoryManager memoryManager, List<KBufferDescriptor> list)
|
||||||
|
{
|
||||||
|
foreach (KBufferDescriptor descriptor in list)
|
||||||
|
{
|
||||||
|
KernelResult result = memoryManager.UnmapIpcRestorePermission(
|
||||||
|
descriptor.ClientAddress,
|
||||||
|
descriptor.Size,
|
||||||
|
descriptor.State);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using Ryujinx.HLE.HOS.Services;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
{
|
{
|
||||||
|
@ -10,12 +12,116 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
|
||||||
private KPort _parent;
|
private KPort _parent;
|
||||||
|
|
||||||
public KClientPort(Horizon system) : base(system) { }
|
public bool IsLight => _parent.IsLight;
|
||||||
|
|
||||||
public void Initialize(KPort parent, int maxSessions)
|
private object _countIncLock;
|
||||||
|
|
||||||
|
//TODO: Remove that, we need it for now to allow HLE
|
||||||
|
//SM implementation to work with the new IPC system.
|
||||||
|
public IpcService Service { get; set; }
|
||||||
|
|
||||||
|
public KClientPort(Horizon system, KPort parent, int maxSessions) : base(system)
|
||||||
{
|
{
|
||||||
_maxSessions = maxSessions;
|
_maxSessions = maxSessions;
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
|
|
||||||
|
_countIncLock = new object();
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult Connect(out KClientSession clientSession)
|
||||||
|
{
|
||||||
|
clientSession = null;
|
||||||
|
|
||||||
|
KProcess currentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (currentProcess.ResourceLimit != null &&
|
||||||
|
!currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1))
|
||||||
|
{
|
||||||
|
return KernelResult.ResLimitExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_countIncLock)
|
||||||
|
{
|
||||||
|
if (_sessionsCount < _maxSessions)
|
||||||
|
{
|
||||||
|
_sessionsCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
|
||||||
|
|
||||||
|
return KernelResult.SessionCountExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentCapacity < _sessionsCount)
|
||||||
|
{
|
||||||
|
_currentCapacity = _sessionsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KSession session = new KSession(System);
|
||||||
|
|
||||||
|
if (Service != null)
|
||||||
|
{
|
||||||
|
session.ClientSession.Service = Service;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelResult result = _parent.EnqueueIncomingSession(session.ServerSession);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
session.ClientSession.DecrementReferenceCount();
|
||||||
|
session.ServerSession.DecrementReferenceCount();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientSession = session.ClientSession;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult ConnectLight(out KLightClientSession clientSession)
|
||||||
|
{
|
||||||
|
clientSession = null;
|
||||||
|
|
||||||
|
KProcess currentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (currentProcess.ResourceLimit != null &&
|
||||||
|
!currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1))
|
||||||
|
{
|
||||||
|
return KernelResult.ResLimitExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_countIncLock)
|
||||||
|
{
|
||||||
|
if (_sessionsCount < _maxSessions)
|
||||||
|
{
|
||||||
|
_sessionsCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
|
||||||
|
|
||||||
|
return KernelResult.SessionCountExceeded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KLightSession session = new KLightSession(System);
|
||||||
|
|
||||||
|
KernelResult result = _parent.EnqueueIncomingLightSession(session.ServerSession);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
session.ClientSession.DecrementReferenceCount();
|
||||||
|
session.ServerSession.DecrementReferenceCount();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientSession = session.ClientSession;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public new static KernelResult RemoveName(Horizon system, string name)
|
public new static KernelResult RemoveName(Horizon system, string name)
|
||||||
|
|
60
Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
Normal file
60
Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using Ryujinx.HLE.HOS.Services;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
{
|
||||||
|
class KClientSession : KSynchronizationObject
|
||||||
|
{
|
||||||
|
public KProcess CreatorProcess { get; }
|
||||||
|
|
||||||
|
private KSession _parent;
|
||||||
|
|
||||||
|
public ChannelState State { get; set; }
|
||||||
|
|
||||||
|
//TODO: Remove that, we need it for now to allow HLE
|
||||||
|
//services implementation to work with the new IPC system.
|
||||||
|
public IpcService Service { get; set; }
|
||||||
|
|
||||||
|
public KClientSession(Horizon system, KSession parent) : base(system)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
|
||||||
|
State = ChannelState.Open;
|
||||||
|
|
||||||
|
CreatorProcess = system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
CreatorProcess.IncrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SendSyncRequest(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
|
||||||
|
{
|
||||||
|
KThread currentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
|
KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize);
|
||||||
|
|
||||||
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
|
currentThread.SignaledObj = null;
|
||||||
|
currentThread.ObjSyncResult = KernelResult.Success;
|
||||||
|
|
||||||
|
KernelResult result = _parent.ServerSession.EnqueueRequest(request);
|
||||||
|
|
||||||
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
if (result == KernelResult.Success)
|
||||||
|
{
|
||||||
|
result = currentThread.ObjSyncResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Destroy()
|
||||||
|
{
|
||||||
|
_parent.DisconnectClient();
|
||||||
|
_parent.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs
Normal file
14
Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
{
|
||||||
|
class KLightClientSession : KAutoObject
|
||||||
|
{
|
||||||
|
private KLightSession _parent;
|
||||||
|
|
||||||
|
public KLightClientSession(Horizon system, KLightSession parent) : base(system)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs
Normal file
14
Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
{
|
||||||
|
class KLightServerSession : KAutoObject
|
||||||
|
{
|
||||||
|
private KLightSession _parent;
|
||||||
|
|
||||||
|
public KLightServerSession(Horizon system, KLightSession parent) : base(system)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs
Normal file
20
Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
{
|
||||||
|
class KLightSession : KAutoObject
|
||||||
|
{
|
||||||
|
public KLightServerSession ServerSession { get; }
|
||||||
|
public KLightClientSession ClientSession { get; }
|
||||||
|
|
||||||
|
private bool _hasBeenInitialized;
|
||||||
|
|
||||||
|
public KLightSession(Horizon system) : base(system)
|
||||||
|
{
|
||||||
|
ServerSession = new KLightServerSession(system, this);
|
||||||
|
ClientSession = new KLightClientSession(system, this);
|
||||||
|
|
||||||
|
_hasBeenInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,25 +4,68 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
{
|
{
|
||||||
class KPort : KAutoObject
|
class KPort : KAutoObject
|
||||||
{
|
{
|
||||||
public KServerPort ServerPort { get; private set; }
|
public KServerPort ServerPort { get; }
|
||||||
public KClientPort ClientPort { get; private set; }
|
public KClientPort ClientPort { get; }
|
||||||
|
|
||||||
private long _nameAddress;
|
private long _nameAddress;
|
||||||
private bool _isLight;
|
|
||||||
|
|
||||||
public KPort(Horizon system) : base(system)
|
private ChannelState _state;
|
||||||
|
|
||||||
|
public bool IsLight { get; private set; }
|
||||||
|
|
||||||
|
public KPort(Horizon system, int maxSessions, bool isLight, long nameAddress) : base(system)
|
||||||
{
|
{
|
||||||
ServerPort = new KServerPort(system);
|
ServerPort = new KServerPort(system, this);
|
||||||
ClientPort = new KClientPort(system);
|
ClientPort = new KClientPort(system, this, maxSessions);
|
||||||
|
|
||||||
|
IsLight = isLight;
|
||||||
|
_nameAddress = nameAddress;
|
||||||
|
|
||||||
|
_state = ChannelState.Open;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(int maxSessions, bool isLight, long nameAddress)
|
public KernelResult EnqueueIncomingSession(KServerSession session)
|
||||||
{
|
{
|
||||||
ServerPort.Initialize(this);
|
KernelResult result;
|
||||||
ClientPort.Initialize(this, maxSessions);
|
|
||||||
|
|
||||||
_isLight = isLight;
|
System.CriticalSection.Enter();
|
||||||
_nameAddress = nameAddress;
|
|
||||||
|
if (_state == ChannelState.Open)
|
||||||
|
{
|
||||||
|
ServerPort.EnqueueIncomingSession(session);
|
||||||
|
|
||||||
|
result = KernelResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = KernelResult.PortClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult EnqueueIncomingLightSession(KLightServerSession session)
|
||||||
|
{
|
||||||
|
KernelResult result;
|
||||||
|
|
||||||
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
|
if (_state == ChannelState.Open)
|
||||||
|
{
|
||||||
|
ServerPort.EnqueueIncomingLightSession(session);
|
||||||
|
|
||||||
|
result = KernelResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = KernelResult.PortClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,16 +1,87 @@
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
{
|
{
|
||||||
class KServerPort : KSynchronizationObject
|
class KServerPort : KSynchronizationObject
|
||||||
{
|
{
|
||||||
|
private LinkedList<KServerSession> _incomingConnections;
|
||||||
|
private LinkedList<KLightServerSession> _lightIncomingConnections;
|
||||||
|
|
||||||
private KPort _parent;
|
private KPort _parent;
|
||||||
|
|
||||||
public KServerPort(Horizon system) : base(system) { }
|
public bool IsLight => _parent.IsLight;
|
||||||
|
|
||||||
public void Initialize(KPort parent)
|
public KServerPort(Horizon system, KPort parent) : base(system)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
|
|
||||||
|
_incomingConnections = new LinkedList<KServerSession>();
|
||||||
|
_lightIncomingConnections = new LinkedList<KLightServerSession>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnqueueIncomingSession(KServerSession session)
|
||||||
|
{
|
||||||
|
AcceptIncomingConnection(_incomingConnections, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnqueueIncomingLightSession(KLightServerSession session)
|
||||||
|
{
|
||||||
|
AcceptIncomingConnection(_lightIncomingConnections, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AcceptIncomingConnection<T>(LinkedList<T> list, T session)
|
||||||
|
{
|
||||||
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
|
list.AddLast(session);
|
||||||
|
|
||||||
|
if (list.Count == 1)
|
||||||
|
{
|
||||||
|
Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.CriticalSection.Leave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public KServerSession AcceptIncomingConnection()
|
||||||
|
{
|
||||||
|
return AcceptIncomingConnection(_incomingConnections);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KLightServerSession AcceptIncomingLightConnection()
|
||||||
|
{
|
||||||
|
return AcceptIncomingConnection(_lightIncomingConnections);
|
||||||
|
}
|
||||||
|
|
||||||
|
private T AcceptIncomingConnection<T>(LinkedList<T> list)
|
||||||
|
{
|
||||||
|
T session = default(T);
|
||||||
|
|
||||||
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
|
if (list.Count != 0)
|
||||||
|
{
|
||||||
|
session = list.First.Value;
|
||||||
|
|
||||||
|
list.RemoveFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsSignaled()
|
||||||
|
{
|
||||||
|
if (_parent.IsLight)
|
||||||
|
{
|
||||||
|
return _lightIncomingConnections.Count != 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _incomingConnections.Count != 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
1262
Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
Normal file
1262
Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,18 +1,40 @@
|
||||||
using Ryujinx.HLE.HOS.Services;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
{
|
{
|
||||||
class KSession : IDisposable
|
class KSession : KAutoObject, IDisposable
|
||||||
{
|
{
|
||||||
public IpcService Service { get; private set; }
|
public KServerSession ServerSession { get; }
|
||||||
|
public KClientSession ClientSession { get; }
|
||||||
|
|
||||||
public string ServiceName { get; private set; }
|
private bool _hasBeenInitialized;
|
||||||
|
|
||||||
public KSession(IpcService service, string serviceName)
|
public KSession(Horizon system) : base(system)
|
||||||
{
|
{
|
||||||
Service = service;
|
ServerSession = new KServerSession(system, this);
|
||||||
ServiceName = serviceName;
|
ClientSession = new KClientSession(system, this);
|
||||||
|
|
||||||
|
_hasBeenInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisconnectClient()
|
||||||
|
{
|
||||||
|
if (ClientSession.State == ChannelState.Open)
|
||||||
|
{
|
||||||
|
ClientSession.State = ChannelState.ClientDisconnected;
|
||||||
|
|
||||||
|
ServerSession.CancelAllRequestsClientDisconnected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisconnectServer()
|
||||||
|
{
|
||||||
|
if (ClientSession.State == ChannelState.Open)
|
||||||
|
{
|
||||||
|
ClientSession.State = ChannelState.ServerDisconnected;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -22,10 +44,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing && Service is IDisposable disposableService)
|
if (disposing && ClientSession.Service is IDisposable disposableService)
|
||||||
{
|
{
|
||||||
disposableService.Dispose();
|
disposableService.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Destroy()
|
||||||
|
{
|
||||||
|
if (_hasBeenInitialized)
|
||||||
|
{
|
||||||
|
KProcess creatorProcess = ClientSession.CreatorProcess;
|
||||||
|
|
||||||
|
creatorProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
|
||||||
|
|
||||||
|
creatorProcess.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
31
Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs
Normal file
31
Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
{
|
||||||
|
class KSessionRequest
|
||||||
|
{
|
||||||
|
public KBufferDescriptorTable BufferDescriptorTable { get; }
|
||||||
|
|
||||||
|
public KThread ClientThread { get; }
|
||||||
|
|
||||||
|
public KProcess ServerProcess { get; set; }
|
||||||
|
|
||||||
|
public KWritableEvent AsyncEvent { get; }
|
||||||
|
|
||||||
|
public ulong CustomCmdBuffAddr { get; }
|
||||||
|
public ulong CustomCmdBuffSize { get; }
|
||||||
|
|
||||||
|
public KSessionRequest(
|
||||||
|
KThread clientThread,
|
||||||
|
ulong customCmdBuffAddr,
|
||||||
|
ulong customCmdBuffSize)
|
||||||
|
{
|
||||||
|
ClientThread = clientThread;
|
||||||
|
CustomCmdBuffAddr = customCmdBuffAddr;
|
||||||
|
CustomCmdBuffSize = customCmdBuffSize;
|
||||||
|
|
||||||
|
BufferDescriptorTable = new KBufferDescriptorTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,29 +1,108 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KMemoryBlock
|
class KMemoryBlock
|
||||||
{
|
{
|
||||||
public ulong BaseAddress { get; set; }
|
public ulong BaseAddress { get; private set; }
|
||||||
public ulong PagesCount { get; set; }
|
public ulong PagesCount { get; private set; }
|
||||||
|
|
||||||
public MemoryState State { get; set; }
|
public MemoryState State { get; private set; }
|
||||||
public MemoryPermission Permission { get; set; }
|
public MemoryPermission Permission { get; private set; }
|
||||||
public MemoryAttribute Attribute { get; set; }
|
public MemoryAttribute Attribute { get; private set; }
|
||||||
|
public MemoryPermission SourcePermission { get; private set; }
|
||||||
|
|
||||||
public int IpcRefCount { get; set; }
|
public int IpcRefCount { get; private set; }
|
||||||
public int DeviceRefCount { get; set; }
|
public int DeviceRefCount { get; private set; }
|
||||||
|
|
||||||
public KMemoryBlock(
|
public KMemoryBlock(
|
||||||
ulong baseAddress,
|
ulong baseAddress,
|
||||||
ulong pagesCount,
|
ulong pagesCount,
|
||||||
MemoryState state,
|
MemoryState state,
|
||||||
MemoryPermission permission,
|
MemoryPermission permission,
|
||||||
MemoryAttribute attribute)
|
MemoryAttribute attribute,
|
||||||
|
int ipcRefCount = 0,
|
||||||
|
int deviceRefCount = 0)
|
||||||
{
|
{
|
||||||
BaseAddress = baseAddress;
|
BaseAddress = baseAddress;
|
||||||
PagesCount = pagesCount;
|
PagesCount = pagesCount;
|
||||||
State = state;
|
State = state;
|
||||||
Attribute = attribute;
|
Attribute = attribute;
|
||||||
Permission = permission;
|
Permission = permission;
|
||||||
|
IpcRefCount = ipcRefCount;
|
||||||
|
DeviceRefCount = deviceRefCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetState(MemoryPermission permission, MemoryState state, MemoryAttribute attribute)
|
||||||
|
{
|
||||||
|
Permission = permission;
|
||||||
|
State = state;
|
||||||
|
Attribute &= MemoryAttribute.IpcAndDeviceMapped;
|
||||||
|
Attribute |= attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetIpcMappingPermission(MemoryPermission permission)
|
||||||
|
{
|
||||||
|
int oldIpcRefCount = IpcRefCount++;
|
||||||
|
|
||||||
|
if ((ushort)IpcRefCount == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("IPC reference count increment overflowed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldIpcRefCount == 0)
|
||||||
|
{
|
||||||
|
SourcePermission = permission;
|
||||||
|
|
||||||
|
Permission &= ~MemoryPermission.ReadAndWrite;
|
||||||
|
Permission |= MemoryPermission.ReadAndWrite & permission;
|
||||||
|
}
|
||||||
|
|
||||||
|
Attribute |= MemoryAttribute.IpcMapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RestoreIpcMappingPermission()
|
||||||
|
{
|
||||||
|
int oldIpcRefCount = IpcRefCount--;
|
||||||
|
|
||||||
|
if (oldIpcRefCount == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("IPC reference count decrement underflowed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldIpcRefCount == 1)
|
||||||
|
{
|
||||||
|
Permission = SourcePermission;
|
||||||
|
|
||||||
|
SourcePermission = MemoryPermission.None;
|
||||||
|
|
||||||
|
Attribute &= ~MemoryAttribute.IpcMapped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public KMemoryBlock SplitRightAtAddress(ulong address)
|
||||||
|
{
|
||||||
|
ulong leftAddress = BaseAddress;
|
||||||
|
|
||||||
|
ulong leftPagesCount = (address - leftAddress) / KMemoryManager.PageSize;
|
||||||
|
|
||||||
|
BaseAddress = address;
|
||||||
|
|
||||||
|
PagesCount -= leftPagesCount;
|
||||||
|
|
||||||
|
return new KMemoryBlock(
|
||||||
|
leftAddress,
|
||||||
|
leftPagesCount,
|
||||||
|
State,
|
||||||
|
Permission,
|
||||||
|
Attribute,
|
||||||
|
IpcRefCount,
|
||||||
|
DeviceRefCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddPages(ulong pagesCount)
|
||||||
|
{
|
||||||
|
PagesCount += pagesCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KMemoryInfo GetInfo()
|
public KMemoryInfo GetInfo()
|
||||||
|
@ -36,6 +115,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
State,
|
State,
|
||||||
Permission,
|
Permission,
|
||||||
Attribute,
|
Attribute,
|
||||||
|
SourcePermission,
|
||||||
IpcRefCount,
|
IpcRefCount,
|
||||||
DeviceRefCount);
|
DeviceRefCount);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KMemoryInfo
|
class KMemoryInfo
|
||||||
{
|
{
|
||||||
public ulong Address { get; private set; }
|
public ulong Address { get; }
|
||||||
public ulong Size { get; private set; }
|
public ulong Size { get; }
|
||||||
|
|
||||||
public MemoryState State { get; private set; }
|
public MemoryState State { get; }
|
||||||
public MemoryPermission Permission { get; private set; }
|
public MemoryPermission Permission { get; }
|
||||||
public MemoryAttribute Attribute { get; private set; }
|
public MemoryAttribute Attribute { get; }
|
||||||
|
public MemoryPermission SourcePermission { get; }
|
||||||
|
|
||||||
public int IpcRefCount { get; private set; }
|
public int IpcRefCount { get; }
|
||||||
public int DeviceRefCount { get; private set; }
|
public int DeviceRefCount { get; }
|
||||||
|
|
||||||
public KMemoryInfo(
|
public KMemoryInfo(
|
||||||
ulong address,
|
ulong address,
|
||||||
|
@ -18,16 +19,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
MemoryState state,
|
MemoryState state,
|
||||||
MemoryPermission permission,
|
MemoryPermission permission,
|
||||||
MemoryAttribute attribute,
|
MemoryAttribute attribute,
|
||||||
|
MemoryPermission sourcePermission,
|
||||||
int ipcRefCount,
|
int ipcRefCount,
|
||||||
int deviceRefCount)
|
int deviceRefCount)
|
||||||
{
|
{
|
||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
State = state;
|
State = state;
|
||||||
Attribute = attribute;
|
Permission = permission;
|
||||||
Permission = permission;
|
Attribute = attribute;
|
||||||
IpcRefCount = ipcRefCount;
|
SourcePermission = sourcePermission;
|
||||||
DeviceRefCount = deviceRefCount;
|
IpcRefCount = ipcRefCount;
|
||||||
|
DeviceRefCount = deviceRefCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -94,6 +94,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ulong AllocatePagesContiguous(ulong pagesCount, bool backwards)
|
||||||
|
{
|
||||||
|
lock (_blocks)
|
||||||
|
{
|
||||||
|
return AllocatePagesContiguousImpl(pagesCount, backwards);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private KernelResult AllocatePagesImpl(ulong pagesCount, bool backwards, out KPageList pageList)
|
private KernelResult AllocatePagesImpl(ulong pagesCount, bool backwards, out KPageList pageList)
|
||||||
{
|
{
|
||||||
pageList = new KPageList();
|
pageList = new KPageList();
|
||||||
|
@ -122,113 +130,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
//If so, try allocating as much requested pages as possible.
|
//If so, try allocating as much requested pages as possible.
|
||||||
while (blockPagesCount <= pagesCount)
|
while (blockPagesCount <= pagesCount)
|
||||||
{
|
{
|
||||||
ulong address = 0;
|
ulong address = AllocatePagesForOrder(blockIndex, backwards, bestFitBlockSize);
|
||||||
|
|
||||||
for (int currBlockIndex = blockIndex;
|
|
||||||
currBlockIndex < _blockOrdersCount && address == 0;
|
|
||||||
currBlockIndex++)
|
|
||||||
{
|
|
||||||
block = _blocks[currBlockIndex];
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
bool zeroMask = false;
|
|
||||||
|
|
||||||
for (int level = 0; level < block.MaxLevel; level++)
|
|
||||||
{
|
|
||||||
long mask = block.Masks[level][index];
|
|
||||||
|
|
||||||
if (mask == 0)
|
|
||||||
{
|
|
||||||
zeroMask = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backwards)
|
|
||||||
{
|
|
||||||
index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
block.FreeCount--;
|
|
||||||
|
|
||||||
int tempIdx = index;
|
|
||||||
|
|
||||||
for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
|
|
||||||
{
|
|
||||||
block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
|
|
||||||
|
|
||||||
if (block.Masks[level][tempIdx / 64] != 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
address = block.StartAligned + ((ulong)index << block.Order);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int currBlockIndex = blockIndex;
|
|
||||||
currBlockIndex < _blockOrdersCount && address == 0;
|
|
||||||
currBlockIndex++)
|
|
||||||
{
|
|
||||||
block = _blocks[currBlockIndex];
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
bool zeroMask = false;
|
|
||||||
|
|
||||||
for (int level = 0; level < block.MaxLevel; level++)
|
|
||||||
{
|
|
||||||
long mask = block.Masks[level][index];
|
|
||||||
|
|
||||||
if (mask == 0)
|
|
||||||
{
|
|
||||||
zeroMask = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backwards)
|
|
||||||
{
|
|
||||||
index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
block.FreeCount--;
|
|
||||||
|
|
||||||
int tempIdx = index;
|
|
||||||
|
|
||||||
for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
|
|
||||||
{
|
|
||||||
block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
|
|
||||||
|
|
||||||
if (block.Masks[level][tempIdx / 64] != 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
address = block.StartAligned + ((ulong)index << block.Order);
|
|
||||||
}
|
|
||||||
|
|
||||||
//The address being zero means that no free space was found on that order,
|
//The address being zero means that no free space was found on that order,
|
||||||
//just give up and try with the next one.
|
//just give up and try with the next one.
|
||||||
|
@ -237,15 +139,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//If we are using a larger order than best fit, then we should
|
|
||||||
//split it into smaller blocks.
|
|
||||||
ulong firstFreeBlockSize = 1UL << block.Order;
|
|
||||||
|
|
||||||
if (firstFreeBlockSize > bestFitBlockSize)
|
|
||||||
{
|
|
||||||
FreePages(address + bestFitBlockSize, (firstFreeBlockSize - bestFitBlockSize) / KMemoryManager.PageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Add new allocated page(s) to the pages list.
|
//Add new allocated page(s) to the pages list.
|
||||||
//If an error occurs, then free all allocated pages and fail.
|
//If an error occurs, then free all allocated pages and fail.
|
||||||
KernelResult result = pageList.AddRange(address, blockPagesCount);
|
KernelResult result = pageList.AddRange(address, blockPagesCount);
|
||||||
|
@ -283,6 +176,172 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
return KernelResult.OutOfMemory;
|
return KernelResult.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ulong AllocatePagesContiguousImpl(ulong pagesCount, bool backwards)
|
||||||
|
{
|
||||||
|
if (pagesCount == 0 || _blocks.Length < 1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockIndex = 0;
|
||||||
|
|
||||||
|
while ((1UL << _blocks[blockIndex].Order) / KMemoryManager.PageSize < pagesCount)
|
||||||
|
{
|
||||||
|
if (++blockIndex >= _blocks.Length)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong tightestFitBlockSize = 1UL << _blocks[blockIndex].Order;
|
||||||
|
|
||||||
|
ulong address = AllocatePagesForOrder(blockIndex, backwards, tightestFitBlockSize);
|
||||||
|
|
||||||
|
ulong requiredSize = pagesCount * KMemoryManager.PageSize;
|
||||||
|
|
||||||
|
if (address != 0 && tightestFitBlockSize > requiredSize)
|
||||||
|
{
|
||||||
|
FreePages(address + requiredSize, (tightestFitBlockSize - requiredSize) / KMemoryManager.PageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ulong AllocatePagesForOrder(int blockIndex, bool backwards, ulong bestFitBlockSize)
|
||||||
|
{
|
||||||
|
ulong address = 0;
|
||||||
|
|
||||||
|
KMemoryRegionBlock block = null;
|
||||||
|
|
||||||
|
for (int currBlockIndex = blockIndex;
|
||||||
|
currBlockIndex < _blockOrdersCount && address == 0;
|
||||||
|
currBlockIndex++)
|
||||||
|
{
|
||||||
|
block = _blocks[currBlockIndex];
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
bool zeroMask = false;
|
||||||
|
|
||||||
|
for (int level = 0; level < block.MaxLevel; level++)
|
||||||
|
{
|
||||||
|
long mask = block.Masks[level][index];
|
||||||
|
|
||||||
|
if (mask == 0)
|
||||||
|
{
|
||||||
|
zeroMask = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backwards)
|
||||||
|
{
|
||||||
|
index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
block.FreeCount--;
|
||||||
|
|
||||||
|
int tempIdx = index;
|
||||||
|
|
||||||
|
for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
|
||||||
|
{
|
||||||
|
block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
|
||||||
|
|
||||||
|
if (block.Masks[level][tempIdx / 64] != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address = block.StartAligned + ((ulong)index << block.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int currBlockIndex = blockIndex;
|
||||||
|
currBlockIndex < _blockOrdersCount && address == 0;
|
||||||
|
currBlockIndex++)
|
||||||
|
{
|
||||||
|
block = _blocks[currBlockIndex];
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
bool zeroMask = false;
|
||||||
|
|
||||||
|
for (int level = 0; level < block.MaxLevel; level++)
|
||||||
|
{
|
||||||
|
long mask = block.Masks[level][index];
|
||||||
|
|
||||||
|
if (mask == 0)
|
||||||
|
{
|
||||||
|
zeroMask = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backwards)
|
||||||
|
{
|
||||||
|
index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
block.FreeCount--;
|
||||||
|
|
||||||
|
int tempIdx = index;
|
||||||
|
|
||||||
|
for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
|
||||||
|
{
|
||||||
|
block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
|
||||||
|
|
||||||
|
if (block.Masks[level][tempIdx / 64] != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address = block.StartAligned + ((ulong)index << block.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address != 0)
|
||||||
|
{
|
||||||
|
//If we are using a larger order than best fit, then we should
|
||||||
|
//split it into smaller blocks.
|
||||||
|
ulong firstFreeBlockSize = 1UL << block.Order;
|
||||||
|
|
||||||
|
if (firstFreeBlockSize > bestFitBlockSize)
|
||||||
|
{
|
||||||
|
FreePages(address + bestFitBlockSize, (firstFreeBlockSize - bestFitBlockSize) / KMemoryManager.PageSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FreePage(ulong address)
|
||||||
|
{
|
||||||
|
lock (_blocks)
|
||||||
|
{
|
||||||
|
FreePages(address, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void FreePages(KPageList pageList)
|
public void FreePages(KPageList pageList)
|
||||||
{
|
{
|
||||||
lock (_blocks)
|
lock (_blocks)
|
||||||
|
|
|
@ -4,7 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KSharedMemory
|
class KSharedMemory : KAutoObject
|
||||||
{
|
{
|
||||||
private KPageList _pageList;
|
private KPageList _pageList;
|
||||||
|
|
||||||
|
@ -14,10 +14,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
private MemoryPermission _userPermission;
|
private MemoryPermission _userPermission;
|
||||||
|
|
||||||
public KSharedMemory(
|
public KSharedMemory(
|
||||||
|
Horizon system,
|
||||||
KPageList pageList,
|
KPageList pageList,
|
||||||
long ownerPid,
|
long ownerPid,
|
||||||
MemoryPermission ownerPermission,
|
MemoryPermission ownerPermission,
|
||||||
MemoryPermission userPermission)
|
MemoryPermission userPermission) : base(system)
|
||||||
{
|
{
|
||||||
_pageList = pageList;
|
_pageList = pageList;
|
||||||
_ownerPid = ownerPid;
|
_ownerPid = ownerPid;
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KTransferMemory
|
class KTransferMemory : KAutoObject
|
||||||
{
|
{
|
||||||
public ulong Address { get; private set; }
|
public ulong Address { get; private set; }
|
||||||
public ulong Size { get; private set; }
|
public ulong Size { get; private set; }
|
||||||
|
|
||||||
public KTransferMemory(ulong address, ulong size)
|
public KTransferMemory(Horizon system, ulong address, ulong size) : base(system)
|
||||||
{
|
{
|
||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Process
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
class KHandleEntry
|
class KHandleEntry
|
||||||
|
@ -6,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
public int Index { get; private set; }
|
public int Index { get; private set; }
|
||||||
|
|
||||||
public ushort HandleId { get; set; }
|
public ushort HandleId { get; set; }
|
||||||
public object Obj { get; set; }
|
public KAutoObject Obj { get; set; }
|
||||||
|
|
||||||
public KHandleEntry(int index)
|
public KHandleEntry(int index)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,8 +6,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
class KHandleTable
|
class KHandleTable
|
||||||
{
|
{
|
||||||
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
|
public const int SelfThreadHandle = (0x1ffff << 15) | 0;
|
||||||
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
|
public const int SelfProcessHandle = (0x1ffff << 15) | 1;
|
||||||
|
|
||||||
private Horizon _system;
|
private Horizon _system;
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult GenerateHandle(object obj, out int handle)
|
public KernelResult GenerateHandle(KAutoObject obj, out int handle)
|
||||||
{
|
{
|
||||||
handle = 0;
|
handle = 0;
|
||||||
|
|
||||||
|
@ -85,7 +85,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
_activeSlotsCount++;
|
_activeSlotsCount++;
|
||||||
|
|
||||||
handle = (int)((_idCounter << 15) & 0xffff8000) | entry.Index;
|
handle = (_idCounter << 15) | entry.Index;
|
||||||
|
|
||||||
|
obj.IncrementReferenceCount();
|
||||||
|
|
||||||
if ((short)(_idCounter + 1) >= 0)
|
if ((short)(_idCounter + 1) >= 0)
|
||||||
{
|
{
|
||||||
|
@ -100,6 +102,72 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult ReserveHandle(out int handle)
|
||||||
|
{
|
||||||
|
handle = 0;
|
||||||
|
|
||||||
|
lock (_table)
|
||||||
|
{
|
||||||
|
if (_activeSlotsCount >= _size)
|
||||||
|
{
|
||||||
|
return KernelResult.HandleTableFull;
|
||||||
|
}
|
||||||
|
|
||||||
|
KHandleEntry entry = _nextFreeEntry;
|
||||||
|
|
||||||
|
_nextFreeEntry = entry.Next;
|
||||||
|
|
||||||
|
_activeSlotsCount++;
|
||||||
|
|
||||||
|
handle = (_idCounter << 15) | entry.Index;
|
||||||
|
|
||||||
|
if ((short)(_idCounter + 1) >= 0)
|
||||||
|
{
|
||||||
|
_idCounter++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_idCounter = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelHandleReservation(int handle)
|
||||||
|
{
|
||||||
|
int index = (handle >> 0) & 0x7fff;
|
||||||
|
int handleId = (handle >> 15);
|
||||||
|
|
||||||
|
lock (_table)
|
||||||
|
{
|
||||||
|
KHandleEntry entry = _table[index];
|
||||||
|
|
||||||
|
entry.Obj = null;
|
||||||
|
entry.Next = _nextFreeEntry;
|
||||||
|
|
||||||
|
_nextFreeEntry = entry;
|
||||||
|
|
||||||
|
_activeSlotsCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetReservedHandleObj(int handle, KAutoObject obj)
|
||||||
|
{
|
||||||
|
int index = (handle >> 0) & 0x7fff;
|
||||||
|
int handleId = (handle >> 15);
|
||||||
|
|
||||||
|
lock (_table)
|
||||||
|
{
|
||||||
|
KHandleEntry entry = _table[index];
|
||||||
|
|
||||||
|
entry.Obj = obj;
|
||||||
|
entry.HandleId = (ushort)(handle >> 15);
|
||||||
|
|
||||||
|
obj.IncrementReferenceCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool CloseHandle(int handle)
|
public bool CloseHandle(int handle)
|
||||||
{
|
{
|
||||||
if ((handle >> 30) != 0 ||
|
if ((handle >> 30) != 0 ||
|
||||||
|
@ -112,6 +180,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
int index = (handle >> 0) & 0x7fff;
|
int index = (handle >> 0) & 0x7fff;
|
||||||
int handleId = (handle >> 15);
|
int handleId = (handle >> 15);
|
||||||
|
|
||||||
|
KAutoObject obj = null;
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
lock (_table)
|
lock (_table)
|
||||||
|
@ -120,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
KHandleEntry entry = _table[index];
|
KHandleEntry entry = _table[index];
|
||||||
|
|
||||||
if (entry.Obj != null && entry.HandleId == handleId)
|
if ((obj = entry.Obj) != null && entry.HandleId == handleId)
|
||||||
{
|
{
|
||||||
entry.Obj = null;
|
entry.Obj = null;
|
||||||
entry.Next = _nextFreeEntry;
|
entry.Next = _nextFreeEntry;
|
||||||
|
@ -134,17 +204,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
obj.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T GetObject<T>(int handle)
|
public T GetObject<T>(int handle) where T : KAutoObject
|
||||||
{
|
{
|
||||||
int index = (handle >> 0) & 0x7fff;
|
int index = (handle >> 0) & 0x7fff;
|
||||||
int handleId = (handle >> 15);
|
int handleId = (handle >> 15);
|
||||||
|
|
||||||
lock (_table)
|
lock (_table)
|
||||||
{
|
{
|
||||||
if ((handle >> 30) == 0 && handleId != 0)
|
if ((handle >> 30) == 0 && handleId != 0 && index < _size)
|
||||||
{
|
{
|
||||||
KHandleEntry entry = _table[index];
|
KHandleEntry entry = _table[index];
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
private SortedDictionary<ulong, KTlsPageInfo> _fullTlsPages;
|
private SortedDictionary<ulong, KTlsPageInfo> _fullTlsPages;
|
||||||
private SortedDictionary<ulong, KTlsPageInfo> _freeTlsPages;
|
private SortedDictionary<ulong, KTlsPageInfo> _freeTlsPages;
|
||||||
|
|
||||||
public int DefaultCpuCore { get; private set; }
|
public int DefaultCpuCore { get; set; }
|
||||||
|
|
||||||
public bool Debug { get; private set; }
|
public bool Debug { get; private set; }
|
||||||
|
|
||||||
|
@ -557,14 +557,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
private KernelResult FreeTlsPage(KTlsPageInfo pageInfo)
|
private KernelResult FreeTlsPage(KTlsPageInfo pageInfo)
|
||||||
{
|
{
|
||||||
KernelResult result = MemoryManager.ConvertVaToPa(pageInfo.PageAddr, out ulong tlsPagePa);
|
if (!MemoryManager.ConvertVaToPa(pageInfo.PageAddr, out ulong tlsPagePa))
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Unexpected failure translating virtual address to physical.");
|
throw new InvalidOperationException("Unexpected failure translating virtual address to physical.");
|
||||||
}
|
}
|
||||||
|
|
||||||
result = MemoryManager.UnmapForKernel(pageInfo.PageAddr, 1, MemoryState.ThreadLocal);
|
KernelResult result = MemoryManager.UnmapForKernel(pageInfo.PageAddr, 1, MemoryState.ThreadLocal);
|
||||||
|
|
||||||
if (result == KernelResult.Success)
|
if (result == KernelResult.Success)
|
||||||
{
|
{
|
||||||
|
@ -636,9 +634,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
void CleanUpForError()
|
void CleanUpForError()
|
||||||
{
|
{
|
||||||
mainThread?.Terminate();
|
|
||||||
HandleTable.Destroy();
|
HandleTable.Destroy();
|
||||||
|
|
||||||
|
mainThread?.DecrementReferenceCount();
|
||||||
|
|
||||||
if (_mainThreadStackSize != 0)
|
if (_mainThreadStackSize != 0)
|
||||||
{
|
{
|
||||||
ulong stackBottom = stackTop - _mainThreadStackSize;
|
ulong stackBottom = stackTop - _mainThreadStackSize;
|
||||||
|
@ -646,6 +645,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
ulong stackPagesCount = _mainThreadStackSize / KMemoryManager.PageSize;
|
ulong stackPagesCount = _mainThreadStackSize / KMemoryManager.PageSize;
|
||||||
|
|
||||||
MemoryManager.UnmapForKernel(stackBottom, stackPagesCount, MemoryState.Stack);
|
MemoryManager.UnmapForKernel(stackBottom, stackPagesCount, MemoryState.Stack);
|
||||||
|
|
||||||
|
_mainThreadStackSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryResourceLimit?.Release(LimitableResource.Memory, stackSizeRounded);
|
memoryResourceLimit?.Release(LimitableResource.Memory, stackSizeRounded);
|
||||||
|
@ -756,6 +757,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
mainThread.Reschedule(ThreadSchedState.Running);
|
mainThread.Reschedule(ThreadSchedState.Running);
|
||||||
|
|
||||||
|
if (result == KernelResult.Success)
|
||||||
|
{
|
||||||
|
mainThread.IncrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
mainThread.DecrementReferenceCount();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,6 +306,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
{
|
{
|
||||||
int range = max - min + 1;
|
int range = max - min + 1;
|
||||||
|
|
||||||
|
if (range == 64)
|
||||||
|
{
|
||||||
|
return -1L;
|
||||||
|
}
|
||||||
|
|
||||||
long mask = (1L << range) - 1;
|
long mask = (1L << range) - 1;
|
||||||
|
|
||||||
return mask << min;
|
return mask << min;
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
using ChocolArm64.Events;
|
using ChocolArm64.Events;
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
@ -16,26 +13,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
private Horizon _system;
|
private Horizon _system;
|
||||||
private MemoryManager _memory;
|
private MemoryManager _memory;
|
||||||
|
|
||||||
private struct HleIpcMessage
|
|
||||||
{
|
|
||||||
public KThread Thread { get; private set; }
|
|
||||||
public KSession Session { get; private set; }
|
|
||||||
public IpcMessage Message { get; private set; }
|
|
||||||
public long MessagePtr { get; private set; }
|
|
||||||
|
|
||||||
public HleIpcMessage(
|
|
||||||
KThread thread,
|
|
||||||
KSession session,
|
|
||||||
IpcMessage message,
|
|
||||||
long messagePtr)
|
|
||||||
{
|
|
||||||
Thread = thread;
|
|
||||||
Session = session;
|
|
||||||
Message = message;
|
|
||||||
MessagePtr = messagePtr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SvcHandler(Switch device, KProcess process)
|
public SvcHandler(Switch device, KProcess process)
|
||||||
{
|
{
|
||||||
_device = device;
|
_device = device;
|
||||||
|
|
532
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs
Normal file
532
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs
Normal file
|
@ -0,0 +1,532 @@
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
{
|
||||||
|
partial class SvcHandler
|
||||||
|
{
|
||||||
|
private struct HleIpcMessage
|
||||||
|
{
|
||||||
|
public KThread Thread { get; private set; }
|
||||||
|
public KClientSession Session { get; private set; }
|
||||||
|
public IpcMessage Message { get; private set; }
|
||||||
|
public long MessagePtr { get; private set; }
|
||||||
|
|
||||||
|
public HleIpcMessage(
|
||||||
|
KThread thread,
|
||||||
|
KClientSession session,
|
||||||
|
IpcMessage message,
|
||||||
|
long messagePtr)
|
||||||
|
{
|
||||||
|
Thread = thread;
|
||||||
|
Session = session;
|
||||||
|
Message = message;
|
||||||
|
MessagePtr = messagePtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult ConnectToNamedPort64(ulong namePtr, out int handle)
|
||||||
|
{
|
||||||
|
return ConnectToNamedPort(namePtr, out handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult ConnectToNamedPort(ulong namePtr, out int handle)
|
||||||
|
{
|
||||||
|
handle = 0;
|
||||||
|
|
||||||
|
if (!KernelTransfer.UserToKernelString(_system, namePtr, 12, out string name))
|
||||||
|
{
|
||||||
|
return KernelResult.UserCopyFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.Length > 11)
|
||||||
|
{
|
||||||
|
return KernelResult.MaximumExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
KAutoObject autoObj = KAutoObject.FindNamedObject(_system, name);
|
||||||
|
|
||||||
|
if (!(autoObj is KClientPort clientPort))
|
||||||
|
{
|
||||||
|
return KernelResult.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KernelResult result = currentProcess.HandleTable.ReserveHandle(out handle);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = clientPort.Connect(out KClientSession clientSession);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
currentProcess.HandleTable.CancelHandleReservation(handle);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentProcess.HandleTable.SetReservedHandleObj(handle, clientSession);
|
||||||
|
|
||||||
|
clientSession.DecrementReferenceCount();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SendSyncRequest64(int handle)
|
||||||
|
{
|
||||||
|
return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.ThreadState.Tpidr, 0x100, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SendSyncRequestWithUserBuffer64(ulong messagePtr, ulong size, int handle)
|
||||||
|
{
|
||||||
|
return SendSyncRequest(messagePtr, size, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult SendSyncRequest(ulong messagePtr, ulong size, int handle)
|
||||||
|
{
|
||||||
|
byte[] messageData = _memory.ReadBytes((long)messagePtr, (long)size);
|
||||||
|
|
||||||
|
KClientSession clientSession = _process.HandleTable.GetObject<KClientSession>(handle);
|
||||||
|
|
||||||
|
if (clientSession == null || clientSession.Service == null)
|
||||||
|
{
|
||||||
|
return SendSyncRequest_(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientSession != null)
|
||||||
|
{
|
||||||
|
_system.CriticalSection.Enter();
|
||||||
|
|
||||||
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
|
currentThread.SignaledObj = null;
|
||||||
|
currentThread.ObjSyncResult = KernelResult.Success;
|
||||||
|
|
||||||
|
currentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
|
IpcMessage message = new IpcMessage(messageData, (long)messagePtr);
|
||||||
|
|
||||||
|
ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage(
|
||||||
|
currentThread,
|
||||||
|
clientSession,
|
||||||
|
message,
|
||||||
|
(long)messagePtr));
|
||||||
|
|
||||||
|
_system.ThreadCounter.AddCount();
|
||||||
|
|
||||||
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
|
return currentThread.ObjSyncResult;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!");
|
||||||
|
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessIpcRequest(object state)
|
||||||
|
{
|
||||||
|
HleIpcMessage ipcMessage = (HleIpcMessage)state;
|
||||||
|
|
||||||
|
ipcMessage.Thread.ObjSyncResult = IpcHandler.IpcCall(
|
||||||
|
_device,
|
||||||
|
_process,
|
||||||
|
_memory,
|
||||||
|
ipcMessage.Session,
|
||||||
|
ipcMessage.Message,
|
||||||
|
ipcMessage.MessagePtr);
|
||||||
|
|
||||||
|
_system.ThreadCounter.Signal();
|
||||||
|
|
||||||
|
ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult SendSyncRequest_(int handle)
|
||||||
|
{
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle);
|
||||||
|
|
||||||
|
if (session == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return session.SendSyncRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult CreateSession64(
|
||||||
|
bool isLight,
|
||||||
|
ulong namePtr,
|
||||||
|
out int serverSessionHandle,
|
||||||
|
out int clientSessionHandle)
|
||||||
|
{
|
||||||
|
return CreateSession(isLight, namePtr, out serverSessionHandle, out clientSessionHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult CreateSession(
|
||||||
|
bool isLight,
|
||||||
|
ulong namePtr,
|
||||||
|
out int serverSessionHandle,
|
||||||
|
out int clientSessionHandle)
|
||||||
|
{
|
||||||
|
serverSessionHandle = 0;
|
||||||
|
clientSessionHandle = 0;
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KResourceLimit resourceLimit = currentProcess.ResourceLimit;
|
||||||
|
|
||||||
|
KernelResult result = KernelResult.Success;
|
||||||
|
|
||||||
|
if (resourceLimit != null && !resourceLimit.Reserve(LimitableResource.Session, 1))
|
||||||
|
{
|
||||||
|
return KernelResult.ResLimitExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLight)
|
||||||
|
{
|
||||||
|
KLightSession session = new KLightSession(_system);
|
||||||
|
|
||||||
|
result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle);
|
||||||
|
|
||||||
|
if (result == KernelResult.Success)
|
||||||
|
{
|
||||||
|
result = currentProcess.HandleTable.GenerateHandle(session.ClientSession, out clientSessionHandle);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
currentProcess.HandleTable.CloseHandle(serverSessionHandle);
|
||||||
|
|
||||||
|
serverSessionHandle = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session.ServerSession.DecrementReferenceCount();
|
||||||
|
session.ClientSession.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
KSession session = new KSession(_system);
|
||||||
|
|
||||||
|
result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle);
|
||||||
|
|
||||||
|
if (result == KernelResult.Success)
|
||||||
|
{
|
||||||
|
result = currentProcess.HandleTable.GenerateHandle(session.ClientSession, out clientSessionHandle);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
currentProcess.HandleTable.CloseHandle(serverSessionHandle);
|
||||||
|
|
||||||
|
serverSessionHandle = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session.ServerSession.DecrementReferenceCount();
|
||||||
|
session.ClientSession.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult AcceptSession64(int portHandle, out int sessionHandle)
|
||||||
|
{
|
||||||
|
return AcceptSession(portHandle, out sessionHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult AcceptSession(int portHandle, out int sessionHandle)
|
||||||
|
{
|
||||||
|
sessionHandle = 0;
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KServerPort serverPort = currentProcess.HandleTable.GetObject<KServerPort>(portHandle);
|
||||||
|
|
||||||
|
if (serverPort == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelResult result = currentProcess.HandleTable.ReserveHandle(out int handle);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
KAutoObject session;
|
||||||
|
|
||||||
|
if (serverPort.IsLight)
|
||||||
|
{
|
||||||
|
session = serverPort.AcceptIncomingLightConnection();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
session = serverPort.AcceptIncomingConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session != null)
|
||||||
|
{
|
||||||
|
currentProcess.HandleTable.SetReservedHandleObj(handle, session);
|
||||||
|
|
||||||
|
session.DecrementReferenceCount();
|
||||||
|
|
||||||
|
sessionHandle = handle;
|
||||||
|
|
||||||
|
result = KernelResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentProcess.HandleTable.CancelHandleReservation(handle);
|
||||||
|
|
||||||
|
result = KernelResult.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult ReplyAndReceive64(
|
||||||
|
ulong handlesPtr,
|
||||||
|
int handlesCount,
|
||||||
|
int replyTargetHandle,
|
||||||
|
long timeout,
|
||||||
|
out int handleIndex)
|
||||||
|
{
|
||||||
|
handleIndex = 0;
|
||||||
|
|
||||||
|
if ((uint)handlesCount > 0x40)
|
||||||
|
{
|
||||||
|
return KernelResult.MaximumExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
ulong copySize = (ulong)((long)handlesCount * 4);
|
||||||
|
|
||||||
|
if (!currentProcess.MemoryManager.InsideAddrSpace(handlesPtr, copySize))
|
||||||
|
{
|
||||||
|
return KernelResult.UserCopyFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlesPtr + copySize < handlesPtr)
|
||||||
|
{
|
||||||
|
return KernelResult.UserCopyFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] handles = new int[handlesCount];
|
||||||
|
|
||||||
|
if (!KernelTransfer.UserToKernelInt32Array(_system, handlesPtr, handles))
|
||||||
|
{
|
||||||
|
return KernelResult.UserCopyFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
KSynchronizationObject[] syncObjs = new KSynchronizationObject[handlesCount];
|
||||||
|
|
||||||
|
for (int index = 0; index < handlesCount; index++)
|
||||||
|
{
|
||||||
|
KSynchronizationObject obj = currentProcess.HandleTable.GetObject<KSynchronizationObject>(handles[index]);
|
||||||
|
|
||||||
|
if (obj == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
syncObjs[index] = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelResult result;
|
||||||
|
|
||||||
|
if (replyTargetHandle != 0)
|
||||||
|
{
|
||||||
|
KServerSession replyTarget = currentProcess.HandleTable.GetObject<KServerSession>(replyTargetHandle);
|
||||||
|
|
||||||
|
if (replyTarget == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = replyTarget.Reply();
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((result = _system.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success)
|
||||||
|
{
|
||||||
|
KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]);
|
||||||
|
|
||||||
|
if (session == null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result = session.Receive()) != KernelResult.NotFound)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult CreatePort64(
|
||||||
|
int maxSessions,
|
||||||
|
bool isLight,
|
||||||
|
ulong namePtr,
|
||||||
|
out int serverPortHandle,
|
||||||
|
out int clientPortHandle)
|
||||||
|
{
|
||||||
|
return CreatePort(maxSessions, isLight, namePtr, out serverPortHandle, out clientPortHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult CreatePort(
|
||||||
|
int maxSessions,
|
||||||
|
bool isLight,
|
||||||
|
ulong namePtr,
|
||||||
|
out int serverPortHandle,
|
||||||
|
out int clientPortHandle)
|
||||||
|
{
|
||||||
|
serverPortHandle = clientPortHandle = 0;
|
||||||
|
|
||||||
|
if (maxSessions < 1)
|
||||||
|
{
|
||||||
|
return KernelResult.MaximumExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
KPort port = new KPort(_system, maxSessions, isLight, (long)namePtr);
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out serverPortHandle);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
currentProcess.HandleTable.CloseHandle(clientPortHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult ManageNamedPort64(ulong namePtr, int maxSessions, out int handle)
|
||||||
|
{
|
||||||
|
return ManageNamedPort(namePtr, maxSessions, out handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult ManageNamedPort(ulong namePtr, int maxSessions, out int handle)
|
||||||
|
{
|
||||||
|
handle = 0;
|
||||||
|
|
||||||
|
if (!KernelTransfer.UserToKernelString(_system, namePtr, 12, out string name))
|
||||||
|
{
|
||||||
|
return KernelResult.UserCopyFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxSessions < 0 || name.Length > 11)
|
||||||
|
{
|
||||||
|
return KernelResult.MaximumExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxSessions == 0)
|
||||||
|
{
|
||||||
|
return KClientPort.RemoveName(_system, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
KPort port = new KPort(_system, maxSessions, false, 0);
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = port.ClientPort.SetName(name);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
currentProcess.HandleTable.CloseHandle(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult ConnectToPort64(int clientPortHandle, out int clientSessionHandle)
|
||||||
|
{
|
||||||
|
return ConnectToPort(clientPortHandle, out clientSessionHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult ConnectToPort(int clientPortHandle, out int clientSessionHandle)
|
||||||
|
{
|
||||||
|
clientSessionHandle = 0;
|
||||||
|
|
||||||
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KClientPort clientPort = currentProcess.HandleTable.GetObject<KClientPort>(clientPortHandle);
|
||||||
|
|
||||||
|
if (clientPort == null)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelResult result = currentProcess.HandleTable.ReserveHandle(out int handle);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
KAutoObject session;
|
||||||
|
|
||||||
|
if (clientPort.IsLight)
|
||||||
|
{
|
||||||
|
result = clientPort.ConnectLight(out KLightClientSession clientSession);
|
||||||
|
|
||||||
|
session = clientSession;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = clientPort.Connect(out KClientSession clientSession);
|
||||||
|
|
||||||
|
session = clientSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
currentProcess.HandleTable.CancelHandleReservation(handle);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentProcess.HandleTable.SetReservedHandleObj(handle, session);
|
||||||
|
|
||||||
|
session.DecrementReferenceCount();
|
||||||
|
|
||||||
|
clientSessionHandle = handle;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -305,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
KTransferMemory transferMemory = new KTransferMemory(address, size);
|
KTransferMemory transferMemory = new KTransferMemory(_system, address, size);
|
||||||
|
|
||||||
return _process.HandleTable.GenerateHandle(transferMemory, out handle);
|
return _process.HandleTable.GenerateHandle(transferMemory, out handle);
|
||||||
}
|
}
|
||||||
|
@ -350,7 +350,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
|
||||||
public KernelResult UnmapPhysicalMemory64(ulong address, ulong size)
|
public KernelResult UnmapPhysicalMemory64(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
return MapPhysicalMemory(address, size);
|
return UnmapPhysicalMemory(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult UnmapPhysicalMemory(ulong address, ulong size)
|
private KernelResult UnmapPhysicalMemory(ulong address, ulong size)
|
||||||
|
|
|
@ -2,14 +2,11 @@ using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.Services;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
{
|
{
|
||||||
|
@ -82,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
|
||||||
private KernelResult CloseHandle(int handle)
|
private KernelResult CloseHandle(int handle)
|
||||||
{
|
{
|
||||||
object obj = _process.HandleTable.GetObject<object>(handle);
|
KAutoObject obj = _process.HandleTable.GetObject<KAutoObject>(handle);
|
||||||
|
|
||||||
_process.HandleTable.CloseHandle(handle);
|
_process.HandleTable.CloseHandle(handle);
|
||||||
|
|
||||||
|
@ -144,88 +141,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
return _system.Scheduler.GetCurrentThread().Context.ThreadState.CntpctEl0;
|
return _system.Scheduler.GetCurrentThread().Context.ThreadState.CntpctEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult ConnectToNamedPort64(ulong namePtr, out int handle)
|
|
||||||
{
|
|
||||||
return ConnectToNamedPort(namePtr, out handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private KernelResult ConnectToNamedPort(ulong namePtr, out int handle)
|
|
||||||
{
|
|
||||||
string name = MemoryHelper.ReadAsciiString(_memory, (long)namePtr, 8);
|
|
||||||
|
|
||||||
//TODO: Validate that app has perms to access the service, and that the service
|
|
||||||
//actually exists, return error codes otherwise.
|
|
||||||
KSession session = new KSession(ServiceFactory.MakeService(_system, name), name);
|
|
||||||
|
|
||||||
return _process.HandleTable.GenerateHandle(session, out handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SendSyncRequest64(int handle)
|
|
||||||
{
|
|
||||||
return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.ThreadState.Tpidr, 0x100, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SendSyncRequestWithUserBuffer64(ulong messagePtr, ulong size, int handle)
|
|
||||||
{
|
|
||||||
return SendSyncRequest(messagePtr, size, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private KernelResult SendSyncRequest(ulong messagePtr, ulong size, int handle)
|
|
||||||
{
|
|
||||||
byte[] messageData = _memory.ReadBytes((long)messagePtr, (long)size);
|
|
||||||
|
|
||||||
KSession session = _process.HandleTable.GetObject<KSession>(handle);
|
|
||||||
|
|
||||||
if (session != null)
|
|
||||||
{
|
|
||||||
_system.CriticalSection.Enter();
|
|
||||||
|
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
|
||||||
|
|
||||||
currentThread.SignaledObj = null;
|
|
||||||
currentThread.ObjSyncResult = KernelResult.Success;
|
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
|
||||||
|
|
||||||
IpcMessage message = new IpcMessage(messageData, (long)messagePtr);
|
|
||||||
|
|
||||||
ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage(
|
|
||||||
currentThread,
|
|
||||||
session,
|
|
||||||
message,
|
|
||||||
(long)messagePtr));
|
|
||||||
|
|
||||||
_system.ThreadCounter.AddCount();
|
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
|
||||||
|
|
||||||
return currentThread.ObjSyncResult;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!");
|
|
||||||
|
|
||||||
return KernelResult.InvalidHandle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessIpcRequest(object state)
|
|
||||||
{
|
|
||||||
HleIpcMessage ipcMessage = (HleIpcMessage)state;
|
|
||||||
|
|
||||||
ipcMessage.Thread.ObjSyncResult = IpcHandler.IpcCall(
|
|
||||||
_device,
|
|
||||||
_process,
|
|
||||||
_memory,
|
|
||||||
ipcMessage.Session,
|
|
||||||
ipcMessage.Message,
|
|
||||||
ipcMessage.MessagePtr);
|
|
||||||
|
|
||||||
_system.ThreadCounter.Signal();
|
|
||||||
|
|
||||||
ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetProcessId64(int handle, out long pid)
|
public KernelResult GetProcessId64(int handle, out long pid)
|
||||||
{
|
{
|
||||||
return GetProcessId(handle, out pid);
|
return GetProcessId(handle, out pid);
|
||||||
|
@ -664,99 +579,5 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult CreatePort64(
|
|
||||||
int maxSessions,
|
|
||||||
bool isLight,
|
|
||||||
ulong namePtr,
|
|
||||||
out int serverPortHandle,
|
|
||||||
out int clientPortHandle)
|
|
||||||
{
|
|
||||||
return CreatePort(maxSessions, isLight, namePtr, out serverPortHandle, out clientPortHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private KernelResult CreatePort(
|
|
||||||
int maxSessions,
|
|
||||||
bool isLight,
|
|
||||||
ulong namePtr,
|
|
||||||
out int serverPortHandle,
|
|
||||||
out int clientPortHandle)
|
|
||||||
{
|
|
||||||
serverPortHandle = clientPortHandle = 0;
|
|
||||||
|
|
||||||
if (maxSessions < 1)
|
|
||||||
{
|
|
||||||
return KernelResult.MaximumExceeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
KPort port = new KPort(_system);
|
|
||||||
|
|
||||||
port.Initialize(maxSessions, isLight, (long)namePtr);
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out serverPortHandle);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
currentProcess.HandleTable.CloseHandle(clientPortHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ManageNamedPort64(ulong namePtr, int maxSessions, out int handle)
|
|
||||||
{
|
|
||||||
return ManageNamedPort(namePtr, maxSessions, out handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private KernelResult ManageNamedPort(ulong namePtr, int maxSessions, out int handle)
|
|
||||||
{
|
|
||||||
handle = 0;
|
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelString(_system, namePtr, 12, out string name))
|
|
||||||
{
|
|
||||||
return KernelResult.UserCopyFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxSessions < 0 || name.Length > 11)
|
|
||||||
{
|
|
||||||
return KernelResult.MaximumExceeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxSessions == 0)
|
|
||||||
{
|
|
||||||
return KClientPort.RemoveName(_system, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
KPort port = new KPort(_system);
|
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
||||||
|
|
||||||
KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
port.Initialize(maxSessions, false, 0);
|
|
||||||
|
|
||||||
result = port.SetName(name);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
currentProcess.HandleTable.CloseHandle(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,11 +63,15 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
{ 0x33, nameof(SvcHandler.GetThreadContext364) },
|
{ 0x33, nameof(SvcHandler.GetThreadContext364) },
|
||||||
{ 0x34, nameof(SvcHandler.WaitForAddress64) },
|
{ 0x34, nameof(SvcHandler.WaitForAddress64) },
|
||||||
{ 0x35, nameof(SvcHandler.SignalToAddress64) },
|
{ 0x35, nameof(SvcHandler.SignalToAddress64) },
|
||||||
|
{ 0x40, nameof(SvcHandler.CreateSession64) },
|
||||||
|
{ 0x41, nameof(SvcHandler.AcceptSession64) },
|
||||||
|
{ 0x43, nameof(SvcHandler.ReplyAndReceive64) },
|
||||||
{ 0x45, nameof(SvcHandler.CreateEvent64) },
|
{ 0x45, nameof(SvcHandler.CreateEvent64) },
|
||||||
{ 0x65, nameof(SvcHandler.GetProcessList64) },
|
{ 0x65, nameof(SvcHandler.GetProcessList64) },
|
||||||
{ 0x6f, nameof(SvcHandler.GetSystemInfo64) },
|
{ 0x6f, nameof(SvcHandler.GetSystemInfo64) },
|
||||||
{ 0x70, nameof(SvcHandler.CreatePort64) },
|
{ 0x70, nameof(SvcHandler.CreatePort64) },
|
||||||
{ 0x71, nameof(SvcHandler.ManageNamedPort64) }
|
{ 0x71, nameof(SvcHandler.ManageNamedPort64) },
|
||||||
|
{ 0x72, nameof(SvcHandler.ConnectToPort64) }
|
||||||
};
|
};
|
||||||
|
|
||||||
_svcTable64 = new Action<SvcHandler, CpuThreadState>[0x80];
|
_svcTable64 = new Action<SvcHandler, CpuThreadState>[0x80];
|
||||||
|
|
|
@ -62,21 +62,16 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
priority,
|
priority,
|
||||||
cpuCore);
|
cpuCore);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result == KernelResult.Success)
|
||||||
|
{
|
||||||
|
result = _process.HandleTable.GenerateHandle(thread, out handle);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
|
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = _process.HandleTable.GenerateHandle(thread, out handle);
|
thread.DecrementReferenceCount();
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
|
||||||
thread.Terminate();
|
|
||||||
|
|
||||||
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -88,11 +83,22 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
|
||||||
private KernelResult StartThread(int handle)
|
private KernelResult StartThread(int handle)
|
||||||
{
|
{
|
||||||
KThread thread = _process.HandleTable.GetObject<KThread>(handle);
|
KThread thread = _process.HandleTable.GetKThread(handle);
|
||||||
|
|
||||||
if (thread != null)
|
if (thread != null)
|
||||||
{
|
{
|
||||||
return thread.Start();
|
thread.IncrementReferenceCount();
|
||||||
|
|
||||||
|
KernelResult result = thread.Start();
|
||||||
|
|
||||||
|
if (result == KernelResult.Success)
|
||||||
|
{
|
||||||
|
thread.IncrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
thread.DecrementReferenceCount();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
public KEvent(Horizon system)
|
public KEvent(Horizon system)
|
||||||
{
|
{
|
||||||
ReadableEvent = new KReadableEvent(system, this);
|
ReadableEvent = new KReadableEvent(system, this);
|
||||||
WritableEvent = new KWritableEvent(this);
|
WritableEvent = new KWritableEvent(system, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -210,9 +210,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return GetDummyThread();
|
||||||
|
|
||||||
throw new InvalidOperationException("Current thread is not scheduled!");
|
throw new InvalidOperationException("Current thread is not scheduled!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private KThread _dummyThread;
|
||||||
|
|
||||||
|
private KThread GetDummyThread()
|
||||||
|
{
|
||||||
|
if (_dummyThread != null)
|
||||||
|
{
|
||||||
|
return _dummyThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess dummyProcess = new KProcess(_system);
|
||||||
|
|
||||||
|
KThread dummyThread = new KThread(_system);
|
||||||
|
|
||||||
|
dummyThread.Initialize(0, 0, 0, 44, 0, dummyProcess, ThreadType.Dummy);
|
||||||
|
|
||||||
|
return _dummyThread = dummyThread;
|
||||||
|
}
|
||||||
|
|
||||||
public KProcess GetCurrentProcess()
|
public KProcess GetCurrentProcess()
|
||||||
{
|
{
|
||||||
return GetCurrentThread().Owner;
|
return GetCurrentThread().Owner;
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
_system.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeout == 0)
|
if (timeout == 0)
|
||||||
|
|
|
@ -30,6 +30,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
private ulong _tlsAddress;
|
private ulong _tlsAddress;
|
||||||
|
|
||||||
|
public ulong TlsAddress => _tlsAddress;
|
||||||
|
public ulong TlsDramAddress { get; private set; }
|
||||||
|
|
||||||
public long LastScheduledTime { get; set; }
|
public long LastScheduledTime { get; set; }
|
||||||
|
|
||||||
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
|
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
|
||||||
|
@ -67,6 +70,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
public bool WaitingSync { get; set; }
|
public bool WaitingSync { get; set; }
|
||||||
|
|
||||||
private bool _hasExited;
|
private bool _hasExited;
|
||||||
|
private bool _hasBeenInitialized;
|
||||||
|
private bool _hasBeenReleased;
|
||||||
|
|
||||||
public bool WaitingInArbitration { get; set; }
|
public bool WaitingInArbitration { get; set; }
|
||||||
|
|
||||||
|
@ -124,6 +129,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
return KernelResult.OutOfMemory;
|
return KernelResult.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TlsDramAddress = owner.MemoryManager.GetDramAddressFromVa(_tlsAddress);
|
||||||
|
|
||||||
MemoryHelper.FillWithZeros(owner.CpuMemory, (long)_tlsAddress, KTlsPageInfo.TlsEntrySize);
|
MemoryHelper.FillWithZeros(owner.CpuMemory, (long)_tlsAddress, KTlsPageInfo.TlsEntrySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +140,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
Owner = owner;
|
Owner = owner;
|
||||||
|
|
||||||
|
owner.IncrementReferenceCount();
|
||||||
owner.IncrementThreadCount();
|
owner.IncrementThreadCount();
|
||||||
|
|
||||||
is64Bits = (owner.MmuFlags & 1) != 0;
|
is64Bits = (owner.MmuFlags & 1) != 0;
|
||||||
|
@ -156,6 +164,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
ThreadUid = System.GetThreadUid();
|
ThreadUid = System.GetThreadUid();
|
||||||
|
|
||||||
|
_hasBeenInitialized = true;
|
||||||
|
|
||||||
if (owner != null)
|
if (owner != null)
|
||||||
{
|
{
|
||||||
owner.AddThread(this);
|
owner.AddThread(this);
|
||||||
|
@ -252,6 +262,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
public void Exit()
|
public void Exit()
|
||||||
{
|
{
|
||||||
|
//TODO: Debug event.
|
||||||
|
|
||||||
|
if (Owner != null)
|
||||||
|
{
|
||||||
|
Owner.ResourceLimit?.Release(LimitableResource.Thread, 0, 1);
|
||||||
|
|
||||||
|
_hasBeenReleased = true;
|
||||||
|
}
|
||||||
|
|
||||||
System.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
_forcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
|
_forcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
|
||||||
|
@ -259,6 +278,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
ExitImpl();
|
ExitImpl();
|
||||||
|
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
DecrementReferenceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExitImpl()
|
private void ExitImpl()
|
||||||
|
@ -930,7 +951,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Remove from old queues.
|
//Remove thread from the old priority queues.
|
||||||
for (int core = 0; core < KScheduler.CpuCoresCount; core++)
|
for (int core = 0; core < KScheduler.CpuCoresCount; core++)
|
||||||
{
|
{
|
||||||
if (((oldAffinityMask >> core) & 1) != 0)
|
if (((oldAffinityMask >> core) & 1) != 0)
|
||||||
|
@ -946,7 +967,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Insert on new queues.
|
//Add thread to the new priority queues.
|
||||||
for (int core = 0; core < KScheduler.CpuCoresCount; core++)
|
for (int core = 0; core < KScheduler.CpuCoresCount; core++)
|
||||||
{
|
{
|
||||||
if (((AffinityMask >> core) & 1) != 0)
|
if (((AffinityMask >> core) & 1) != 0)
|
||||||
|
@ -965,11 +986,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
_scheduler.ThreadReselectionRequested = true;
|
_scheduler.ThreadReselectionRequested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsSignaled()
|
|
||||||
{
|
|
||||||
return _hasExited;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetEntryArguments(long argsPtr, int threadHandle)
|
public void SetEntryArguments(long argsPtr, int threadHandle)
|
||||||
{
|
{
|
||||||
Context.ThreadState.X0 = (ulong)argsPtr;
|
Context.ThreadState.X0 = (ulong)argsPtr;
|
||||||
|
@ -994,13 +1010,36 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
private void ThreadFinishedHandler(object sender, EventArgs e)
|
private void ThreadFinishedHandler(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
System.Scheduler.ExitThread(this);
|
System.Scheduler.ExitThread(this);
|
||||||
|
|
||||||
Terminate();
|
|
||||||
|
|
||||||
System.Scheduler.RemoveThread(this);
|
System.Scheduler.RemoveThread(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Terminate()
|
public override bool IsSignaled()
|
||||||
|
{
|
||||||
|
return _hasExited;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Destroy()
|
||||||
|
{
|
||||||
|
if (_hasBeenInitialized)
|
||||||
|
{
|
||||||
|
FreeResources();
|
||||||
|
|
||||||
|
bool released = Owner != null || _hasBeenReleased;
|
||||||
|
|
||||||
|
if (Owner != null)
|
||||||
|
{
|
||||||
|
Owner.ResourceLimit?.Release(LimitableResource.Thread, 1, released ? 0 : 1);
|
||||||
|
|
||||||
|
Owner.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
System.ResourceLimit.Release(LimitableResource.Thread, 1, released ? 0 : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FreeResources()
|
||||||
{
|
{
|
||||||
Owner?.RemoveThread(this);
|
Owner?.RemoveThread(this);
|
||||||
|
|
||||||
|
@ -1011,8 +1050,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
System.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
//Wake up all threads that may be waiting for a mutex being held
|
//Wake up all threads that may be waiting for a mutex being held by this thread.
|
||||||
//by this thread.
|
|
||||||
foreach (KThread thread in _mutexWaiters)
|
foreach (KThread thread in _mutexWaiters)
|
||||||
{
|
{
|
||||||
thread.MutexOwner = null;
|
thread.MutexOwner = null;
|
||||||
|
|
|
@ -2,11 +2,11 @@ using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
class KWritableEvent
|
class KWritableEvent : KAutoObject
|
||||||
{
|
{
|
||||||
private KEvent _parent;
|
private KEvent _parent;
|
||||||
|
|
||||||
public KWritableEvent(KEvent parent)
|
public KWritableEvent(Horizon system, KEvent parent) : base(system)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,11 +63,11 @@ namespace Ryujinx.HLE.HOS
|
||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
MemoryRegion memRegion = kip.IsService
|
MemoryRegion memoryRegion = kip.IsService
|
||||||
? MemoryRegion.Service
|
? MemoryRegion.Service
|
||||||
: MemoryRegion.Application;
|
: MemoryRegion.Application;
|
||||||
|
|
||||||
KMemoryRegionManager region = system.MemoryRegions[(int)memRegion];
|
KMemoryRegionManager region = system.MemoryRegions[(int)memoryRegion];
|
||||||
|
|
||||||
KernelResult result = region.AllocatePages((ulong)codePagesCount, false, out KPageList pageList);
|
KernelResult result = region.AllocatePages((ulong)codePagesCount, false, out KPageList pageList);
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
kip.Capabilities,
|
kip.Capabilities,
|
||||||
pageList,
|
pageList,
|
||||||
system.ResourceLimit,
|
system.ResourceLimit,
|
||||||
memRegion);
|
memoryRegion);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
|
@ -103,6 +103,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process.DefaultCpuCore = kip.DefaultProcessorId;
|
||||||
|
|
||||||
result = process.Start(kip.MainThreadPriority, (ulong)kip.MainThreadStackSize);
|
result = process.Start(kip.MainThreadPriority, (ulong)kip.MainThreadStackSize);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
|
@ -201,11 +203,20 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
KProcess process = new KProcess(system);
|
KProcess process = new KProcess(system);
|
||||||
|
|
||||||
|
MemoryRegion memoryRegion = (MemoryRegion)((metaData.Acid.Flags >> 2) & 0xf);
|
||||||
|
|
||||||
|
if (memoryRegion > MemoryRegion.NvServices)
|
||||||
|
{
|
||||||
|
Logger.PrintError(LogClass.Loader, $"Process initialization failed due to invalid ACID flags.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
result = process.Initialize(
|
result = process.Initialize(
|
||||||
creationInfo,
|
creationInfo,
|
||||||
metaData.Aci0.KernelAccessControl.Capabilities,
|
metaData.Aci0.KernelAccessControl.Capabilities,
|
||||||
resourceLimit,
|
resourceLimit,
|
||||||
MemoryRegion.Application);
|
memoryRegion);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
|
@ -228,6 +239,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process.DefaultCpuCore = metaData.DefaultCpuId;
|
||||||
|
|
||||||
result = process.Start(metaData.MainThreadPriority, (ulong)metaData.MainThreadStackSize);
|
result = process.Start(metaData.MainThreadPriority, (ulong)metaData.MainThreadStackSize);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
|
|
|
@ -8,24 +8,24 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
class ServiceCtx
|
class ServiceCtx
|
||||||
{
|
{
|
||||||
public Switch Device { get; private set; }
|
public Switch Device { get; }
|
||||||
public KProcess Process { get; private set; }
|
public KProcess Process { get; }
|
||||||
public MemoryManager Memory { get; private set; }
|
public MemoryManager Memory { get; }
|
||||||
public KSession Session { get; private set; }
|
public KClientSession Session { get; }
|
||||||
public IpcMessage Request { get; private set; }
|
public IpcMessage Request { get; }
|
||||||
public IpcMessage Response { get; private set; }
|
public IpcMessage Response { get; }
|
||||||
public BinaryReader RequestData { get; private set; }
|
public BinaryReader RequestData { get; }
|
||||||
public BinaryWriter ResponseData { get; private set; }
|
public BinaryWriter ResponseData { get; }
|
||||||
|
|
||||||
public ServiceCtx(
|
public ServiceCtx(
|
||||||
Switch device,
|
Switch device,
|
||||||
KProcess process,
|
KProcess process,
|
||||||
MemoryManager memory,
|
MemoryManager memory,
|
||||||
KSession session,
|
KClientSession session,
|
||||||
IpcMessage request,
|
IpcMessage request,
|
||||||
IpcMessage response,
|
IpcMessage response,
|
||||||
BinaryReader requestData,
|
BinaryReader requestData,
|
||||||
BinaryWriter responseData)
|
BinaryWriter responseData)
|
||||||
{
|
{
|
||||||
Device = device;
|
Device = device;
|
||||||
Process = process;
|
Process = process;
|
||||||
|
|
|
@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string dbgMessage = $"{context.Session.ServiceName} {service.GetType().Name}: {commandId}";
|
string dbgMessage = $"{service.GetType().FullName}: {commandId}";
|
||||||
|
|
||||||
throw new ServiceNotImplementedException(context, dbgMessage);
|
throw new ServiceNotImplementedException(context, dbgMessage);
|
||||||
}
|
}
|
||||||
|
@ -132,9 +132,11 @@ namespace Ryujinx.HLE.HOS.Services
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
KSession session = new KSession(obj, context.Session.ServiceName);
|
KSession session = new KSession(context.Device.System);
|
||||||
|
|
||||||
if (context.Process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success)
|
session.ClientSession.Service = obj;
|
||||||
|
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Out of handles!");
|
throw new InvalidOperationException("Out of handles!");
|
||||||
}
|
}
|
||||||
|
@ -151,7 +153,7 @@ namespace Ryujinx.HLE.HOS.Services
|
||||||
{
|
{
|
||||||
int handle = context.Request.HandleDesc.ToMove[index];
|
int handle = context.Request.HandleDesc.ToMove[index];
|
||||||
|
|
||||||
KSession session = context.Process.HandleTable.GetObject<KSession>(handle);
|
KClientSession session = context.Process.HandleTable.GetObject<KClientSession>(handle);
|
||||||
|
|
||||||
return session?.Service is T ? (T)session.Service : null;
|
return session?.Service is T ? (T)session.Service : null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Psm
|
||||||
{
|
{
|
||||||
if (_stateChangeEventHandle == -1)
|
if (_stateChangeEventHandle == -1)
|
||||||
{
|
{
|
||||||
KernelResult resultCode = context.Process.HandleTable.GenerateHandle(_stateChangeEvent, out int stateChangeEventHandle);
|
KernelResult resultCode = context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out int stateChangeEventHandle);
|
||||||
|
|
||||||
if (resultCode != KernelResult.Success)
|
if (resultCode != KernelResult.Success)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Sm
|
namespace Ryujinx.HLE.HOS.Services.Sm
|
||||||
{
|
{
|
||||||
|
@ -12,18 +15,30 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
||||||
|
|
||||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
||||||
|
|
||||||
|
private ConcurrentDictionary<string, KPort> _registeredServices;
|
||||||
|
|
||||||
private bool _isInitialized;
|
private bool _isInitialized;
|
||||||
|
|
||||||
public IUserInterface()
|
public IUserInterface()
|
||||||
{
|
{
|
||||||
_commands = new Dictionary<int, ServiceProcessRequest>
|
_commands = new Dictionary<int, ServiceProcessRequest>
|
||||||
{
|
{
|
||||||
{ 0, Initialize },
|
{ 0, Initialize },
|
||||||
{ 1, GetService }
|
{ 1, GetService },
|
||||||
|
{ 2, RegisterService }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_registeredServices = new ConcurrentDictionary<string, KPort>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int SmNotInitialized = 0x415;
|
public static void InitializePort(Horizon system)
|
||||||
|
{
|
||||||
|
KPort port = new KPort(system, 256, false, 0);
|
||||||
|
|
||||||
|
port.ClientPort.SetName("sm:");
|
||||||
|
|
||||||
|
port.ClientPort.Service = new IUserInterface();
|
||||||
|
}
|
||||||
|
|
||||||
public long Initialize(ServiceCtx context)
|
public long Initialize(ServiceCtx context)
|
||||||
{
|
{
|
||||||
|
@ -34,12 +49,87 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
||||||
|
|
||||||
public long GetService(ServiceCtx context)
|
public long GetService(ServiceCtx context)
|
||||||
{
|
{
|
||||||
//Only for kernel version > 3.0.0.
|
|
||||||
if (!_isInitialized)
|
if (!_isInitialized)
|
||||||
{
|
{
|
||||||
//return SmNotInitialized;
|
return ErrorCode.MakeError(ErrorModule.Sm, SmErr.NotInitialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string name = ReadName(context);
|
||||||
|
|
||||||
|
if (name == string.Empty)
|
||||||
|
{
|
||||||
|
return ErrorCode.MakeError(ErrorModule.Sm, SmErr.InvalidName);
|
||||||
|
}
|
||||||
|
|
||||||
|
KSession session = new KSession(context.Device.System);
|
||||||
|
|
||||||
|
if (_registeredServices.TryGetValue(name, out KPort port))
|
||||||
|
{
|
||||||
|
KernelResult result = port.EnqueueIncomingSession(session.ServerSession);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Session enqueue on port returned error \"{result}\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
session.ClientSession.Service = ServiceFactory.MakeService(context.Device.System, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Out of handles!");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long RegisterService(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (!_isInitialized)
|
||||||
|
{
|
||||||
|
return ErrorCode.MakeError(ErrorModule.Sm, SmErr.NotInitialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
long namePosition = context.RequestData.BaseStream.Position;
|
||||||
|
|
||||||
|
string name = ReadName(context);
|
||||||
|
|
||||||
|
context.RequestData.BaseStream.Seek(namePosition + 8, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
bool isLight = (context.RequestData.ReadInt32() & 1) != 0;
|
||||||
|
|
||||||
|
int maxSessions = context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
if (name == string.Empty)
|
||||||
|
{
|
||||||
|
return ErrorCode.MakeError(ErrorModule.Sm, SmErr.InvalidName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.PrintInfo(LogClass.ServiceSm, $"Register \"{name}\".");
|
||||||
|
|
||||||
|
KPort port = new KPort(context.Device.System, maxSessions, isLight, 0);
|
||||||
|
|
||||||
|
if (!_registeredServices.TryAdd(name, port))
|
||||||
|
{
|
||||||
|
return ErrorCode.MakeError(ErrorModule.Sm, SmErr.AlreadyRegistered);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(port.ServerPort, out int handle) != KernelResult.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Out of handles!");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ReadName(ServiceCtx context)
|
||||||
|
{
|
||||||
string name = string.Empty;
|
string name = string.Empty;
|
||||||
|
|
||||||
for (int index = 0; index < 8 &&
|
for (int index = 0; index < 8 &&
|
||||||
|
@ -54,21 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name == string.Empty)
|
return name;
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
KSession session = new KSession(ServiceFactory.MakeService(context.Device.System, name), name);
|
|
||||||
|
|
||||||
if (context.Process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Out of handles!");
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
9
Ryujinx.HLE/HOS/Services/Sm/SmErr.cs
Normal file
9
Ryujinx.HLE/HOS/Services/Sm/SmErr.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Sm
|
||||||
|
{
|
||||||
|
static class SmErr
|
||||||
|
{
|
||||||
|
public const int NotInitialized = 2;
|
||||||
|
public const int AlreadyRegistered = 4;
|
||||||
|
public const int InvalidName = 6;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Compression
|
namespace Ryujinx.HLE.Loaders.Compression
|
||||||
{
|
{
|
||||||
|
@ -7,22 +6,26 @@ namespace Ryujinx.HLE.Loaders.Compression
|
||||||
{
|
{
|
||||||
private class BackwardsReader
|
private class BackwardsReader
|
||||||
{
|
{
|
||||||
private Stream _baseStream;
|
private byte[] _data;
|
||||||
|
|
||||||
public BackwardsReader(Stream baseStream)
|
private int _position;
|
||||||
|
|
||||||
|
public int Position => _position;
|
||||||
|
|
||||||
|
public BackwardsReader(byte[] data, int end)
|
||||||
{
|
{
|
||||||
_baseStream = baseStream;
|
_data = data;
|
||||||
|
_position = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SeekCurrent(int offset)
|
||||||
|
{
|
||||||
|
_position += offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte ReadByte()
|
public byte ReadByte()
|
||||||
{
|
{
|
||||||
_baseStream.Seek(-1, SeekOrigin.Current);
|
return _data[--_position];
|
||||||
|
|
||||||
byte value = (byte)_baseStream.ReadByte();
|
|
||||||
|
|
||||||
_baseStream.Seek(-1, SeekOrigin.Current);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public short ReadInt16()
|
public short ReadInt16()
|
||||||
|
@ -39,30 +42,24 @@ namespace Ryujinx.HLE.Loaders.Compression
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] Decompress(Stream input, int decompressedLength)
|
public static void DecompressInPlace(byte[] buffer, int headerEnd)
|
||||||
{
|
{
|
||||||
long end = input.Position;
|
BackwardsReader reader = new BackwardsReader(buffer, headerEnd);
|
||||||
|
|
||||||
BackwardsReader reader = new BackwardsReader(input);
|
|
||||||
|
|
||||||
int additionalDecLength = reader.ReadInt32();
|
int additionalDecLength = reader.ReadInt32();
|
||||||
int startOffset = reader.ReadInt32();
|
int startOffset = reader.ReadInt32();
|
||||||
int compressedLength = reader.ReadInt32();
|
int compressedLength = reader.ReadInt32();
|
||||||
|
|
||||||
input.Seek(12 - startOffset, SeekOrigin.Current);
|
reader.SeekCurrent(12 - startOffset);
|
||||||
|
|
||||||
byte[] dec = new byte[decompressedLength];
|
int decBase = headerEnd - compressedLength;
|
||||||
|
|
||||||
int decompressedLengthUnpadded = compressedLength + additionalDecLength;
|
int decPos = compressedLength + additionalDecLength;
|
||||||
|
|
||||||
int decompressionStart = decompressedLength - decompressedLengthUnpadded;
|
|
||||||
|
|
||||||
int decPos = dec.Length;
|
|
||||||
|
|
||||||
byte mask = 0;
|
byte mask = 0;
|
||||||
byte header = 0;
|
byte header = 0;
|
||||||
|
|
||||||
while (decPos > decompressionStart)
|
while (decPos > 0)
|
||||||
{
|
{
|
||||||
if ((mask >>= 1) == 0)
|
if ((mask >>= 1) == 0)
|
||||||
{
|
{
|
||||||
|
@ -72,7 +69,7 @@ namespace Ryujinx.HLE.Loaders.Compression
|
||||||
|
|
||||||
if ((header & mask) == 0)
|
if ((header & mask) == 0)
|
||||||
{
|
{
|
||||||
dec[--decPos] = reader.ReadByte();
|
buffer[decBase + --decPos] = reader.ReadByte();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -81,25 +78,30 @@ namespace Ryujinx.HLE.Loaders.Compression
|
||||||
int length = (pair >> 12) + 3;
|
int length = (pair >> 12) + 3;
|
||||||
int position = (pair & 0xfff) + 3;
|
int position = (pair & 0xfff) + 3;
|
||||||
|
|
||||||
|
if (length > decPos)
|
||||||
|
{
|
||||||
|
length = decPos;
|
||||||
|
}
|
||||||
|
|
||||||
decPos -= length;
|
decPos -= length;
|
||||||
|
|
||||||
|
int dstPos = decBase + decPos;
|
||||||
|
|
||||||
if (length <= position)
|
if (length <= position)
|
||||||
{
|
{
|
||||||
int srcPos = decPos + position;
|
int srcPos = dstPos + position;
|
||||||
|
|
||||||
Buffer.BlockCopy(dec, srcPos, dec, decPos, length);
|
Buffer.BlockCopy(buffer, srcPos, buffer, dstPos, length);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int offset = 0; offset < length; offset++)
|
for (int offset = 0; offset < length; offset++)
|
||||||
{
|
{
|
||||||
dec[decPos + offset] = dec[decPos + position + offset];
|
buffer[dstPos + offset] = buffer[dstPos + position + offset];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dec;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -98,7 +98,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
|
|
||||||
MainThreadStackSize = segments[1].Attribute;
|
MainThreadStackSize = segments[1].Attribute;
|
||||||
|
|
||||||
Capabilities = new int[8];
|
Capabilities = new int[32];
|
||||||
|
|
||||||
for (int index = 0; index < Capabilities.Length; index++)
|
for (int index = 0; index < Capabilities.Length; index++)
|
||||||
{
|
{
|
||||||
|
@ -114,13 +114,11 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
|
|
||||||
private byte[] ReadSegment(SegmentHeader header, Stream input)
|
private byte[] ReadSegment(SegmentHeader header, Stream input)
|
||||||
{
|
{
|
||||||
long end = input.Position + header.CompressedSize;
|
byte[] data = new byte[header.DecompressedSize];
|
||||||
|
|
||||||
input.Seek(end, SeekOrigin.Begin);
|
input.Read(data, 0, header.CompressedSize);
|
||||||
|
|
||||||
byte[] data = BackwardsLz.Decompress(input, header.DecompressedSize);
|
BackwardsLz.DecompressInPlace(data, header.CompressedSize);
|
||||||
|
|
||||||
input.Seek(end, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
||||||
int length = (controlByte & 0x07) + 1;
|
int length = (controlByte & 0x07) + 1;
|
||||||
bool registerAllowed = (controlByte & 0x80) != 0;
|
bool registerAllowed = (controlByte & 0x80) != 0;
|
||||||
|
|
||||||
services.Add(Encoding.ASCII.GetString(reader.ReadBytes(length), 0, length), registerAllowed);
|
services[Encoding.ASCII.GetString(reader.ReadBytes(length))] = registerAllowed;
|
||||||
|
|
||||||
byteReaded += length + 1;
|
byteReaded += length + 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue