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:
gdkchan 2019-01-18 20:26:39 -02:00 committed by GitHub
parent 3731d0ce84
commit 22bacc6188
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 4310 additions and 840 deletions

View File

@ -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()
{
Dispose(true);

View File

@ -7,6 +7,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Sm;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Npdm;
@ -157,8 +158,8 @@ namespace Ryujinx.HLE.HOS
hidPageList .AddRange(hidPa, HidSize / KMemoryManager.PageSize);
fontPageList.AddRange(fontPa, FontSize / KMemoryManager.PageSize);
HidSharedMem = new KSharedMemory(hidPageList, 0, 0, MemoryPermission.Read);
FontSharedMem = new KSharedMemory(fontPageList, 0, 0, MemoryPermission.Read);
HidSharedMem = new KSharedMemory(this, hidPageList, 0, 0, MemoryPermission.Read);
FontSharedMem = new KSharedMemory(this, fontPageList, 0, 0, MemoryPermission.Read);
AppletState = new AppletStateMgr(this);
@ -166,6 +167,8 @@ namespace Ryujinx.HLE.HOS
Font = new SharedFontManager(device, (long)(fontPa - DramMemoryMap.DramBase));
IUserInterface.InitializePort(this);
VsyncEvent = new KEvent(this);
LoadKeySet();
@ -259,6 +262,14 @@ namespace Ryujinx.HLE.HOS
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)
{
if (xci.SecurePartition == null)

View File

@ -10,12 +10,12 @@ namespace Ryujinx.HLE.HOS.Ipc
static class IpcHandler
{
public static KernelResult IpcCall(
Switch device,
KProcess process,
MemoryManager memory,
KSession session,
IpcMessage request,
long cmdPtr)
Switch device,
KProcess process,
MemoryManager memory,
KClientSession session,
IpcMessage request,
long cmdPtr)
{
IpcMessage response = new IpcMessage();

View File

@ -1,12 +1,18 @@
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Common
{
class KAutoObject
{
protected Horizon System;
private int _referenceCount;
public KAutoObject(Horizon system)
{
System = system;
_referenceCount = 1;
}
public virtual KernelResult SetName(string name)
@ -38,5 +44,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
return null;
}
public void IncrementReferenceCount()
{
Interlocked.Increment(ref _referenceCount);
}
public void DecrementReferenceCount()
{
if (Interlocked.Decrement(ref _referenceCount) == 0)
{
Destroy();
}
}
protected virtual void Destroy() { }
}
}

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel.Common
{
class KResourceLimit
class KResourceLimit : KAutoObject
{
private const int Time10SecondsMs = 10000;
@ -18,9 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
private int _waitingThreadsCount;
private Horizon _system;
public KResourceLimit(Horizon system)
public KResourceLimit(Horizon system) : base(system)
{
_current = new long[(int)LimitableResource.Count];
_limit = new long[(int)LimitableResource.Count];
@ -29,8 +27,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
_lockObj = new object();
_waitingThreads = new LinkedList<KThread>();
_system = system;
}
public bool Reserve(LimitableResource resource, ulong amount)
@ -61,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
_waitingThreadsCount++;
KConditionVariable.Wait(_system, _waitingThreads, _lockObj, timeout);
KConditionVariable.Wait(System, _waitingThreads, _lockObj, timeout);
_waitingThreadsCount--;
@ -94,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
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);
@ -105,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
if (_waitingThreadsCount > 0)
{
KConditionVariable.NotifyAll(_system, _waitingThreads);
KConditionVariable.NotifyAll(System, _waitingThreads);
}
}
}

View File

@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
class KSynchronizationObject : KAutoObject
{
public LinkedList<KThread> WaitingThreads;
public LinkedList<KThread> WaitingThreads { get; }
public KSynchronizationObject(Horizon system) : base(system)
{

View File

@ -2,31 +2,36 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
enum KernelResult
{
Success = 0,
InvalidCapability = 0x1c01,
ThreadNotStarted = 0x7201,
ThreadTerminating = 0x7601,
InvalidSize = 0xca01,
InvalidAddress = 0xcc01,
OutOfResource = 0xce01,
OutOfMemory = 0xd001,
HandleTableFull = 0xd201,
InvalidMemState = 0xd401,
InvalidPermission = 0xd801,
InvalidMemRange = 0xdc01,
InvalidPriority = 0xe001,
InvalidCpuCore = 0xe201,
InvalidHandle = 0xe401,
UserCopyFailed = 0xe601,
InvalidCombination = 0xe801,
TimedOut = 0xea01,
Cancelled = 0xec01,
MaximumExceeded = 0xee01,
InvalidEnumValue = 0xf001,
NotFound = 0xf201,
InvalidThread = 0xf401,
InvalidState = 0xfa01,
ReservedValue = 0xfc01,
ResLimitExceeded = 0x10801
Success = 0,
SessionCountExceeded = 0xe01,
InvalidCapability = 0x1c01,
ThreadNotStarted = 0x7201,
ThreadTerminating = 0x7601,
InvalidSize = 0xca01,
InvalidAddress = 0xcc01,
OutOfResource = 0xce01,
OutOfMemory = 0xd001,
HandleTableFull = 0xd201,
InvalidMemState = 0xd401,
InvalidPermission = 0xd801,
InvalidMemRange = 0xdc01,
InvalidPriority = 0xe001,
InvalidCpuCore = 0xe201,
InvalidHandle = 0xe401,
UserCopyFailed = 0xe601,
InvalidCombination = 0xe801,
TimedOut = 0xea01,
Cancelled = 0xec01,
MaximumExceeded = 0xee01,
InvalidEnumValue = 0xf001,
NotFound = 0xf201,
InvalidThread = 0xf401,
PortRemoteClosed = 0xf601,
InvalidState = 0xfa01,
ReservedValue = 0xfc01,
PortClosed = 0x10601,
ResLimitExceeded = 0x10801,
OutOfVaSpace = 0x20601,
CmdBufferTooSmall = 0x20801
}
}

View File

@ -22,6 +22,26 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
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)
{
KProcess currentProcess = system.Scheduler.GetCurrentProcess();

View File

@ -0,0 +1,10 @@
namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
enum ChannelState
{
NotInitialized,
Open,
ClientDisconnected,
ServerDisconnected
}
}

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

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

View File

@ -1,4 +1,6 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Services;
namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
@ -10,12 +12,116 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
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;
_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)

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

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

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

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

View File

@ -4,25 +4,68 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
class KPort : KAutoObject
{
public KServerPort ServerPort { get; private set; }
public KClientPort ClientPort { get; private set; }
public KServerPort ServerPort { get; }
public KClientPort ClientPort { get; }
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);
ClientPort = new KClientPort(system);
ServerPort = new KServerPort(system, this);
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);
ClientPort.Initialize(this, maxSessions);
KernelResult result;
_isLight = isLight;
_nameAddress = nameAddress;
System.CriticalSection.Enter();
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;
}
}
}

View File

@ -1,16 +1,87 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
class KServerPort : KSynchronizationObject
{
private LinkedList<KServerSession> _incomingConnections;
private LinkedList<KLightServerSession> _lightIncomingConnections;
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;
_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;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,40 @@
using Ryujinx.HLE.HOS.Services;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using System;
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;
ServiceName = serviceName;
ServerSession = new KServerSession(system, this);
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()
@ -22,10 +44,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
protected virtual void Dispose(bool disposing)
{
if (disposing && Service is IDisposable disposableService)
if (disposing && ClientSession.Service is IDisposable disposableService)
{
disposableService.Dispose();
}
}
protected override void Destroy()
{
if (_hasBeenInitialized)
{
KProcess creatorProcess = ClientSession.CreatorProcess;
creatorProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
creatorProcess.DecrementReferenceCount();
}
}
}
}

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

View File

@ -1,29 +1,108 @@
using System;
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
class KMemoryBlock
{
public ulong BaseAddress { get; set; }
public ulong PagesCount { get; set; }
public ulong BaseAddress { get; private set; }
public ulong PagesCount { get; private set; }
public MemoryState State { get; set; }
public MemoryPermission Permission { get; set; }
public MemoryAttribute Attribute { get; set; }
public MemoryState State { get; private set; }
public MemoryPermission Permission { get; private set; }
public MemoryAttribute Attribute { get; private set; }
public MemoryPermission SourcePermission { get; private set; }
public int IpcRefCount { get; set; }
public int DeviceRefCount { get; set; }
public int IpcRefCount { get; private set; }
public int DeviceRefCount { get; private set; }
public KMemoryBlock(
ulong baseAddress,
ulong pagesCount,
MemoryState state,
MemoryPermission permission,
MemoryAttribute attribute)
MemoryAttribute attribute,
int ipcRefCount = 0,
int deviceRefCount = 0)
{
BaseAddress = baseAddress;
PagesCount = pagesCount;
State = state;
Attribute = attribute;
Permission = permission;
BaseAddress = baseAddress;
PagesCount = pagesCount;
State = state;
Attribute = attribute;
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()
@ -36,6 +115,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
State,
Permission,
Attribute,
SourcePermission,
IpcRefCount,
DeviceRefCount);
}

View File

@ -2,15 +2,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
class KMemoryInfo
{
public ulong Address { get; private set; }
public ulong Size { get; private set; }
public ulong Address { get; }
public ulong Size { get; }
public MemoryState State { get; private set; }
public MemoryPermission Permission { get; private set; }
public MemoryAttribute Attribute { get; private set; }
public MemoryState State { get; }
public MemoryPermission Permission { get; }
public MemoryAttribute Attribute { get; }
public MemoryPermission SourcePermission { get; }
public int IpcRefCount { get; private set; }
public int DeviceRefCount { get; private set; }
public int IpcRefCount { get; }
public int DeviceRefCount { get; }
public KMemoryInfo(
ulong address,
@ -18,16 +19,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryState state,
MemoryPermission permission,
MemoryAttribute attribute,
MemoryPermission sourcePermission,
int ipcRefCount,
int deviceRefCount)
{
Address = address;
Size = size;
State = state;
Attribute = attribute;
Permission = permission;
IpcRefCount = ipcRefCount;
DeviceRefCount = deviceRefCount;
Address = address;
Size = size;
State = state;
Permission = permission;
Attribute = attribute;
SourcePermission = sourcePermission;
IpcRefCount = ipcRefCount;
DeviceRefCount = deviceRefCount;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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)
{
pageList = new KPageList();
@ -122,113 +130,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
//If so, try allocating as much requested pages as possible.
while (blockPagesCount <= pagesCount)
{
ulong address = 0;
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);
}
ulong address = AllocatePagesForOrder(blockIndex, backwards, bestFitBlockSize);
//The address being zero means that no free space was found on that order,
//just give up and try with the next one.
@ -237,15 +139,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
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.
//If an error occurs, then free all allocated pages and fail.
KernelResult result = pageList.AddRange(address, blockPagesCount);
@ -283,6 +176,172 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
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)
{
lock (_blocks)

View File

@ -4,7 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
class KSharedMemory
class KSharedMemory : KAutoObject
{
private KPageList _pageList;
@ -14,10 +14,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
private MemoryPermission _userPermission;
public KSharedMemory(
Horizon system,
KPageList pageList,
long ownerPid,
MemoryPermission ownerPermission,
MemoryPermission userPermission)
MemoryPermission userPermission) : base(system)
{
_pageList = pageList;
_ownerPid = ownerPid;

View File

@ -1,11 +1,13 @@
using Ryujinx.HLE.HOS.Kernel.Common;
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
class KTransferMemory
class KTransferMemory : KAutoObject
{
public ulong Address { 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;
Size = size;

View File

@ -1,3 +1,5 @@
using Ryujinx.HLE.HOS.Kernel.Common;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KHandleEntry
@ -6,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public int Index { get; private set; }
public ushort HandleId { get; set; }
public object Obj { get; set; }
public ushort HandleId { get; set; }
public KAutoObject Obj { get; set; }
public KHandleEntry(int index)
{

View File

@ -6,8 +6,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KHandleTable
{
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
public const int SelfThreadHandle = (0x1ffff << 15) | 0;
public const int SelfProcessHandle = (0x1ffff << 15) | 1;
private Horizon _system;
@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return KernelResult.Success;
}
public KernelResult GenerateHandle(object obj, out int handle)
public KernelResult GenerateHandle(KAutoObject obj, out int handle)
{
handle = 0;
@ -85,7 +85,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
_activeSlotsCount++;
handle = (int)((_idCounter << 15) & 0xffff8000) | entry.Index;
handle = (_idCounter << 15) | entry.Index;
obj.IncrementReferenceCount();
if ((short)(_idCounter + 1) >= 0)
{
@ -100,6 +102,72 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
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)
{
if ((handle >> 30) != 0 ||
@ -112,6 +180,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
int index = (handle >> 0) & 0x7fff;
int handleId = (handle >> 15);
KAutoObject obj = null;
bool result = false;
lock (_table)
@ -120,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
KHandleEntry entry = _table[index];
if (entry.Obj != null && entry.HandleId == handleId)
if ((obj = entry.Obj) != null && entry.HandleId == handleId)
{
entry.Obj = null;
entry.Next = _nextFreeEntry;
@ -134,17 +204,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
}
}
if (result)
{
obj.DecrementReferenceCount();
}
return result;
}
public T GetObject<T>(int handle)
public T GetObject<T>(int handle) where T : KAutoObject
{
int index = (handle >> 0) & 0x7fff;
int handleId = (handle >> 15);
lock (_table)
{
if ((handle >> 30) == 0 && handleId != 0)
if ((handle >> 30) == 0 && handleId != 0 && index < _size)
{
KHandleEntry entry = _table[index];

View File

@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private SortedDictionary<ulong, KTlsPageInfo> _fullTlsPages;
private SortedDictionary<ulong, KTlsPageInfo> _freeTlsPages;
public int DefaultCpuCore { get; private set; }
public int DefaultCpuCore { get; set; }
public bool Debug { get; private set; }
@ -557,14 +557,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private KernelResult FreeTlsPage(KTlsPageInfo pageInfo)
{
KernelResult result = MemoryManager.ConvertVaToPa(pageInfo.PageAddr, out ulong tlsPagePa);
if (result != KernelResult.Success)
if (!MemoryManager.ConvertVaToPa(pageInfo.PageAddr, out ulong tlsPagePa))
{
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)
{
@ -636,9 +634,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
void CleanUpForError()
{
mainThread?.Terminate();
HandleTable.Destroy();
mainThread?.DecrementReferenceCount();
if (_mainThreadStackSize != 0)
{
ulong stackBottom = stackTop - _mainThreadStackSize;
@ -646,6 +645,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
ulong stackPagesCount = _mainThreadStackSize / KMemoryManager.PageSize;
MemoryManager.UnmapForKernel(stackBottom, stackPagesCount, MemoryState.Stack);
_mainThreadStackSize = 0;
}
memoryResourceLimit?.Release(LimitableResource.Memory, stackSizeRounded);
@ -756,6 +757,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
mainThread.Reschedule(ThreadSchedState.Running);
if (result == KernelResult.Success)
{
mainThread.IncrementReferenceCount();
}
mainThread.DecrementReferenceCount();
return result;
}
}

View File

@ -306,6 +306,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
int range = max - min + 1;
if (range == 64)
{
return -1L;
}
long mask = (1L << range) - 1;
return mask << min;

View File

@ -1,10 +1,7 @@
using ChocolArm64.Events;
using ChocolArm64.Memory;
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.Threading;
using System;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
@ -16,26 +13,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
private Horizon _system;
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)
{
_device = device;

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

View File

@ -305,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
KTransferMemory transferMemory = new KTransferMemory(address, size);
KTransferMemory transferMemory = new KTransferMemory(_system, address, size);
return _process.HandleTable.GenerateHandle(transferMemory, out handle);
}
@ -350,7 +350,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult UnmapPhysicalMemory64(ulong address, ulong size)
{
return MapPhysicalMemory(address, size);
return UnmapPhysicalMemory(address, size);
}
private KernelResult UnmapPhysicalMemory(ulong address, ulong size)

View File

@ -2,14 +2,11 @@ using ChocolArm64.Memory;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Ipc;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
@ -82,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
private KernelResult CloseHandle(int handle)
{
object obj = _process.HandleTable.GetObject<object>(handle);
KAutoObject obj = _process.HandleTable.GetObject<KAutoObject>(handle);
_process.HandleTable.CloseHandle(handle);
@ -144,88 +141,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
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)
{
return GetProcessId(handle, out pid);
@ -664,99 +579,5 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
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;
}
}
}

View File

@ -63,11 +63,15 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ 0x33, nameof(SvcHandler.GetThreadContext364) },
{ 0x34, nameof(SvcHandler.WaitForAddress64) },
{ 0x35, nameof(SvcHandler.SignalToAddress64) },
{ 0x40, nameof(SvcHandler.CreateSession64) },
{ 0x41, nameof(SvcHandler.AcceptSession64) },
{ 0x43, nameof(SvcHandler.ReplyAndReceive64) },
{ 0x45, nameof(SvcHandler.CreateEvent64) },
{ 0x65, nameof(SvcHandler.GetProcessList64) },
{ 0x6f, nameof(SvcHandler.GetSystemInfo64) },
{ 0x70, nameof(SvcHandler.CreatePort64) },
{ 0x71, nameof(SvcHandler.ManageNamedPort64) }
{ 0x71, nameof(SvcHandler.ManageNamedPort64) },
{ 0x72, nameof(SvcHandler.ConnectToPort64) }
};
_svcTable64 = new Action<SvcHandler, CpuThreadState>[0x80];

View File

@ -62,21 +62,16 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
priority,
cpuCore);
if (result != KernelResult.Success)
if (result == KernelResult.Success)
{
result = _process.HandleTable.GenerateHandle(thread, out handle);
}
else
{
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
return result;
}
result = _process.HandleTable.GenerateHandle(thread, out handle);
if (result != KernelResult.Success)
{
thread.Terminate();
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
}
thread.DecrementReferenceCount();
return result;
}
@ -88,11 +83,22 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
private KernelResult StartThread(int handle)
{
KThread thread = _process.HandleTable.GetObject<KThread>(handle);
KThread thread = _process.HandleTable.GetKThread(handle);
if (thread != null)
{
return thread.Start();
thread.IncrementReferenceCount();
KernelResult result = thread.Start();
if (result == KernelResult.Success)
{
thread.IncrementReferenceCount();
}
thread.DecrementReferenceCount();
return result;
}
else
{

View File

@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KEvent(Horizon system)
{
ReadableEvent = new KReadableEvent(system, this);
WritableEvent = new KWritableEvent(this);
WritableEvent = new KWritableEvent(system, this);
}
}
}

View File

@ -210,9 +210,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
}
}
return GetDummyThread();
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()
{
return GetCurrentThread().Owner;

View File

@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_system.CriticalSection.Leave();
return 0;
return KernelResult.Success;
}
if (timeout == 0)

View File

@ -30,6 +30,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private ulong _tlsAddress;
public ulong TlsAddress => _tlsAddress;
public ulong TlsDramAddress { get; private set; }
public long LastScheduledTime { get; set; }
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
@ -67,6 +70,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public bool WaitingSync { get; set; }
private bool _hasExited;
private bool _hasBeenInitialized;
private bool _hasBeenReleased;
public bool WaitingInArbitration { get; set; }
@ -124,6 +129,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return KernelResult.OutOfMemory;
}
TlsDramAddress = owner.MemoryManager.GetDramAddressFromVa(_tlsAddress);
MemoryHelper.FillWithZeros(owner.CpuMemory, (long)_tlsAddress, KTlsPageInfo.TlsEntrySize);
}
@ -133,6 +140,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
Owner = owner;
owner.IncrementReferenceCount();
owner.IncrementThreadCount();
is64Bits = (owner.MmuFlags & 1) != 0;
@ -156,6 +164,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
ThreadUid = System.GetThreadUid();
_hasBeenInitialized = true;
if (owner != null)
{
owner.AddThread(this);
@ -252,6 +262,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public void Exit()
{
//TODO: Debug event.
if (Owner != null)
{
Owner.ResourceLimit?.Release(LimitableResource.Thread, 0, 1);
_hasBeenReleased = true;
}
System.CriticalSection.Enter();
_forcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
@ -259,6 +278,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
ExitImpl();
System.CriticalSection.Leave();
DecrementReferenceCount();
}
private void ExitImpl()
@ -930,7 +951,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return;
}
//Remove from old queues.
//Remove thread from the old priority queues.
for (int core = 0; core < KScheduler.CpuCoresCount; core++)
{
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++)
{
if (((AffinityMask >> core) & 1) != 0)
@ -965,11 +986,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_scheduler.ThreadReselectionRequested = true;
}
public override bool IsSignaled()
{
return _hasExited;
}
public void SetEntryArguments(long argsPtr, int threadHandle)
{
Context.ThreadState.X0 = (ulong)argsPtr;
@ -994,13 +1010,36 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private void ThreadFinishedHandler(object sender, EventArgs e)
{
System.Scheduler.ExitThread(this);
Terminate();
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);
@ -1011,8 +1050,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
System.CriticalSection.Enter();
//Wake up all threads that may be waiting for a mutex being held
//by this thread.
//Wake up all threads that may be waiting for a mutex being held by this thread.
foreach (KThread thread in _mutexWaiters)
{
thread.MutexOwner = null;

View File

@ -2,11 +2,11 @@ using Ryujinx.HLE.HOS.Kernel.Common;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
class KWritableEvent
class KWritableEvent : KAutoObject
{
private KEvent _parent;
public KWritableEvent(KEvent parent)
public KWritableEvent(Horizon system, KEvent parent) : base(system)
{
_parent = parent;
}

View File

@ -63,11 +63,11 @@ namespace Ryujinx.HLE.HOS
0,
0);
MemoryRegion memRegion = kip.IsService
MemoryRegion memoryRegion = kip.IsService
? MemoryRegion.Service
: MemoryRegion.Application;
KMemoryRegionManager region = system.MemoryRegions[(int)memRegion];
KMemoryRegionManager region = system.MemoryRegions[(int)memoryRegion];
KernelResult result = region.AllocatePages((ulong)codePagesCount, false, out KPageList pageList);
@ -85,7 +85,7 @@ namespace Ryujinx.HLE.HOS
kip.Capabilities,
pageList,
system.ResourceLimit,
memRegion);
memoryRegion);
if (result != KernelResult.Success)
{
@ -103,6 +103,8 @@ namespace Ryujinx.HLE.HOS
return false;
}
process.DefaultCpuCore = kip.DefaultProcessorId;
result = process.Start(kip.MainThreadPriority, (ulong)kip.MainThreadStackSize);
if (result != KernelResult.Success)
@ -201,11 +203,20 @@ namespace Ryujinx.HLE.HOS
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(
creationInfo,
metaData.Aci0.KernelAccessControl.Capabilities,
resourceLimit,
MemoryRegion.Application);
memoryRegion);
if (result != KernelResult.Success)
{
@ -228,6 +239,8 @@ namespace Ryujinx.HLE.HOS
}
}
process.DefaultCpuCore = metaData.DefaultCpuId;
result = process.Start(metaData.MainThreadPriority, (ulong)metaData.MainThreadStackSize);
if (result != KernelResult.Success)

View File

@ -8,24 +8,24 @@ namespace Ryujinx.HLE.HOS
{
class ServiceCtx
{
public Switch Device { get; private set; }
public KProcess Process { get; private set; }
public MemoryManager Memory { get; private set; }
public KSession Session { get; private set; }
public IpcMessage Request { get; private set; }
public IpcMessage Response { get; private set; }
public BinaryReader RequestData { get; private set; }
public BinaryWriter ResponseData { get; private set; }
public Switch Device { get; }
public KProcess Process { get; }
public MemoryManager Memory { get; }
public KClientSession Session { get; }
public IpcMessage Request { get; }
public IpcMessage Response { get; }
public BinaryReader RequestData { get; }
public BinaryWriter ResponseData { get; }
public ServiceCtx(
Switch device,
KProcess process,
MemoryManager memory,
KSession session,
IpcMessage request,
IpcMessage response,
BinaryReader requestData,
BinaryWriter responseData)
Switch device,
KProcess process,
MemoryManager memory,
KClientSession session,
IpcMessage request,
IpcMessage response,
BinaryReader requestData,
BinaryWriter responseData)
{
Device = device;
Process = process;

View File

@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services
}
else
{
string dbgMessage = $"{context.Session.ServiceName} {service.GetType().Name}: {commandId}";
string dbgMessage = $"{service.GetType().FullName}: {commandId}";
throw new ServiceNotImplementedException(context, dbgMessage);
}
@ -132,9 +132,11 @@ namespace Ryujinx.HLE.HOS.Services
}
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!");
}
@ -151,7 +153,7 @@ namespace Ryujinx.HLE.HOS.Services
{
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;
}

View File

@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Psm
{
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)
{

View File

@ -1,8 +1,11 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Ipc;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
namespace Ryujinx.HLE.HOS.Services.Sm
{
@ -12,18 +15,30 @@ namespace Ryujinx.HLE.HOS.Services.Sm
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
private ConcurrentDictionary<string, KPort> _registeredServices;
private bool _isInitialized;
public IUserInterface()
{
_commands = new Dictionary<int, ServiceProcessRequest>
{
{ 0, Initialize },
{ 1, GetService }
{ 0, Initialize },
{ 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)
{
@ -34,12 +49,87 @@ namespace Ryujinx.HLE.HOS.Services.Sm
public long GetService(ServiceCtx context)
{
//Only for kernel version > 3.0.0.
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;
for (int index = 0; index < 8 &&
@ -54,21 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
}
}
if (name == string.Empty)
{
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;
return name;
}
}
}

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

View File

@ -1,5 +1,4 @@
using System;
using System.IO;
namespace Ryujinx.HLE.Loaders.Compression
{
@ -7,22 +6,26 @@ namespace Ryujinx.HLE.Loaders.Compression
{
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()
{
_baseStream.Seek(-1, SeekOrigin.Current);
byte value = (byte)_baseStream.ReadByte();
_baseStream.Seek(-1, SeekOrigin.Current);
return value;
return _data[--_position];
}
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(input);
BackwardsReader reader = new BackwardsReader(buffer, headerEnd);
int additionalDecLength = reader.ReadInt32();
int startOffset = 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 decompressionStart = decompressedLength - decompressedLengthUnpadded;
int decPos = dec.Length;
int decPos = compressedLength + additionalDecLength;
byte mask = 0;
byte header = 0;
while (decPos > decompressionStart)
while (decPos > 0)
{
if ((mask >>= 1) == 0)
{
@ -72,7 +69,7 @@ namespace Ryujinx.HLE.Loaders.Compression
if ((header & mask) == 0)
{
dec[--decPos] = reader.ReadByte();
buffer[decBase + --decPos] = reader.ReadByte();
}
else
{
@ -81,25 +78,30 @@ namespace Ryujinx.HLE.Loaders.Compression
int length = (pair >> 12) + 3;
int position = (pair & 0xfff) + 3;
if (length > decPos)
{
length = decPos;
}
decPos -= length;
int dstPos = decBase + decPos;
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
{
for (int offset = 0; offset < length; offset++)
{
dec[decPos + offset] = dec[decPos + position + offset];
buffer[dstPos + offset] = buffer[dstPos + position + offset];
}
}
}
}
return dec;
}
}
}

View File

@ -98,7 +98,7 @@ namespace Ryujinx.HLE.Loaders.Executables
MainThreadStackSize = segments[1].Attribute;
Capabilities = new int[8];
Capabilities = new int[32];
for (int index = 0; index < Capabilities.Length; index++)
{
@ -114,13 +114,11 @@ namespace Ryujinx.HLE.Loaders.Executables
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);
input.Seek(end, SeekOrigin.Begin);
BackwardsLz.DecompressInPlace(data, header.CompressedSize);
return data;
}

View File

@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
int length = (controlByte & 0x07) + 1;
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;
}