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() public void Dispose()
{ {
Dispose(true); 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.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)

View File

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

View File

@ -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() { }
} }
} }

View File

@ -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);
} }
} }
} }

View File

@ -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)
{ {

View File

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

View File

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

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.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)

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

View File

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

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

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

View File

@ -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

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) 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)

View File

@ -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;

View File

@ -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;

View File

@ -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)
{ {

View File

@ -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];

View File

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

View File

@ -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;

View File

@ -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;

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; 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)

View File

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

View File

@ -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];

View File

@ -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
{ {

View File

@ -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);
} }
} }
} }

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

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

View File

@ -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)

View File

@ -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;

View File

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

View File

@ -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)
{ {

View File

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

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

View File

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

View File

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