Implement some IPC related kernel SVCs properly

This commit is contained in:
gdkchan 2018-12-26 16:58:05 -03:00
commit a2237def5a
36 changed files with 3425 additions and 538 deletions

View file

@ -113,6 +113,40 @@ namespace Ryujinx.HLE
} }
} }
public void Set(ulong address, byte value, ulong size)
{
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)
{
ulong size8 = size & ~7UL;
for (ulong offs = 0; offs < size8; offs += 8)
{
WriteUInt64((long)(dst + offs), ReadUInt64((long)(src + offs)));
System.Console.WriteLine((dst + offs).ToString("X16") + " <- " + (src + offs).ToString("X16") + " " + ReadUInt64((long)(src + offs)).ToString("X16"));
}
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

@ -156,8 +156,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);
@ -258,6 +258,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

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

@ -3,6 +3,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
enum KernelResult enum KernelResult
{ {
Success = 0, Success = 0,
SessionCountExceeded = 0xe01,
InvalidCapability = 0x1c01, InvalidCapability = 0x1c01,
ThreadNotStarted = 0x7201, ThreadNotStarted = 0x7201,
ThreadTerminating = 0x7601, ThreadTerminating = 0x7601,
@ -25,8 +26,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
InvalidEnumValue = 0xf001, InvalidEnumValue = 0xf001,
NotFound = 0xf201, NotFound = 0xf201,
InvalidThread = 0xf401, InvalidThread = 0xf401,
PortRemoteClosed = 0xf601,
InvalidState = 0xfa01, InvalidState = 0xfa01,
ReservedValue = 0xfc01, ReservedValue = 0xfc01,
ResLimitExceeded = 0x10801 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,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,5 @@
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
namespace Ryujinx.HLE.HOS.Kernel.Ipc namespace Ryujinx.HLE.HOS.Kernel.Ipc
{ {
@ -10,7 +11,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
private KPort _parent; private KPort _parent;
public KClientPort(Horizon system) : base(system) { } private object _countIncLock;
public KClientPort(Horizon system) : base(system)
{
_countIncLock = new object();
}
public void Initialize(KPort parent, int maxSessions) public void Initialize(KPort parent, int maxSessions)
{ {
@ -18,6 +24,54 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
_parent = parent; _parent = parent;
} }
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);
KernelResult result = _parent.EnqueueIncomingSession(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)
{ {
KAutoObject foundObj = FindNamedObject(system, name); KAutoObject foundObj = FindNamedObject(system, name);

View file

@ -0,0 +1,52 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
class KClientSession : KSynchronizationObject
{
public KProcess CreatorProcess { get; }
private KSession _parent;
public int ResourceStatus { get; private set; }
public KClientSession(Horizon system, KSession parent) : base(system)
{
_parent = parent;
ResourceStatus = 1;
}
public KernelResult SendSyncRequest(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{
KThread currentThread = System.Scheduler.GetCurrentThread();
KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize);
currentThread.IncrementReferenceCount();
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.DecrementReferenceCount();
}
}
}

View file

@ -4,11 +4,13 @@ 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; private int _resourceStatus;
public bool IsLight { get; private set; }
public KPort(Horizon system) : base(system) public KPort(Horizon system) : base(system)
{ {
@ -21,8 +23,32 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
ServerPort.Initialize(this); ServerPort.Initialize(this);
ClientPort.Initialize(this, maxSessions); ClientPort.Initialize(this, maxSessions);
_isLight = isLight; IsLight = isLight;
_nameAddress = nameAddress; _nameAddress = nameAddress;
_resourceStatus = 1;
}
public KernelResult EnqueueIncomingSession(KServerSession session)
{
KernelResult result;
System.CriticalSection.Enter();
if (_resourceStatus == 1)
{
ServerPort.EnqueueIncomingSession(session);
result = KernelResult.Success;
}
else
{
result = KernelResult.PortClosed;
}
System.CriticalSection.Leave();
return result;
} }
} }
} }

View file

@ -1,16 +1,80 @@
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<KServerSession> _lightIncomingConnections;
private KPort _parent; private KPort _parent;
public KServerPort(Horizon system) : base(system) { } public bool IsLight => _parent.IsLight;
public KServerPort(Horizon system) : base(system)
{
_incomingConnections = new LinkedList<KServerSession>();
_lightIncomingConnections = new LinkedList<KServerSession>();
}
public void Initialize(KPort parent) public void Initialize(KPort parent)
{ {
_parent = parent; _parent = parent;
} }
public void EnqueueIncomingSession(KServerSession session)
{
System.CriticalSection.Enter();
_incomingConnections.AddLast(session);
if (_incomingConnections.Count == 1)
{
Signal();
}
System.CriticalSection.Leave();
}
public KServerSession AcceptIncomingConnection()
{
return AcceptIncomingConnection(_incomingConnections);
}
public KServerSession AcceptLightIncomingConnection()
{
return AcceptIncomingConnection(_lightIncomingConnections);
}
private KServerSession AcceptIncomingConnection(LinkedList<KServerSession> list)
{
KServerSession session = null;
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,15 +1,28 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Services; using Ryujinx.HLE.HOS.Services;
using System; using System;
namespace Ryujinx.HLE.HOS.Kernel.Ipc namespace Ryujinx.HLE.HOS.Kernel.Ipc
{ {
class KSession : IDisposable class KSession : KAutoObject, IDisposable
{ {
public KServerSession ServerSession { get; }
public KClientSession ClientSession { get; }
private bool _hasBeenInitialized;
public IpcService Service { get; private set; } public IpcService Service { get; private set; }
public string ServiceName { get; private set; } public string ServiceName { get; private set; }
public KSession(IpcService service, string serviceName) public KSession(Horizon system) : base(system)
{
ServerSession = new KServerSession(system, this);
ClientSession = new KClientSession(system, this);
}
public KSession(Horizon system, IpcService service, string serviceName) : base(system)
{ {
Service = service; Service = service;
ServiceName = serviceName; ServiceName = serviceName;
@ -27,5 +40,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
disposableService.Dispose(); disposableService.Dispose();
} }
} }
protected override void Destroy()
{
if (_hasBeenInitialized)
{
KProcess creatorProcess = ClientSession.CreatorProcess;
creatorProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
}
}
} }
} }

View file

@ -0,0 +1,28 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
class KSessionRequest
{
public KBufferDescriptorTable BufferDescriptorTable { get; }
public KThread SenderThread { get; }
public KWritableEvent AsyncEvent { get; }
public ulong CustomCmdBuffAddr { get; }
public ulong CustomCmdBuffSize { get; }
public KSessionRequest(
KThread senderThread,
ulong customCmdBuffAddr,
ulong customCmdBuffSize)
{
SenderThread = senderThread;
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,14 +19,16 @@ 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;
SourcePermission = sourcePermission;
IpcRefCount = ipcRefCount; IpcRefCount = ipcRefCount;
DeviceRefCount = deviceRefCount; 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
@ -7,7 +9,7 @@ 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

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

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

View file

@ -15,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ {
partial class SvcHandler partial class SvcHandler
{ {
private const bool UseLegacyIpc = true;
public void ExitProcess64() public void ExitProcess64()
{ {
ExitProcess(); ExitProcess();
@ -82,7 +84,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);
@ -146,6 +148,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult ConnectToNamedPort64(ulong namePtr, out int handle) public KernelResult ConnectToNamedPort64(ulong namePtr, out int handle)
{ {
if (!UseLegacyIpc)
{
return ConnectToNamedPort_(namePtr, out handle);
}
return ConnectToNamedPort(namePtr, out handle); return ConnectToNamedPort(namePtr, out handle);
} }
@ -155,13 +162,62 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
//TODO: Validate that app has perms to access the service, and that the service //TODO: Validate that app has perms to access the service, and that the service
//actually exists, return error codes otherwise. //actually exists, return error codes otherwise.
KSession session = new KSession(ServiceFactory.MakeService(_system, name), name); KSession session = new KSession(_system, ServiceFactory.MakeService(_system, name), name);
return _process.HandleTable.GenerateHandle(session, out handle); return _process.HandleTable.GenerateHandle(session, 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 session);
if (result != KernelResult.Success)
{
currentProcess.HandleTable.CancelHandleReservation(handle);
return result;
}
currentProcess.HandleTable.SetReservedHandleObj(handle, session);
return result;
}
public KernelResult SendSyncRequest64(int handle) public KernelResult SendSyncRequest64(int handle)
{ {
if (!UseLegacyIpc)
{
return SendSyncRequest_(handle);
}
return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.ThreadState.Tpidr, 0x100, handle); return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.ThreadState.Tpidr, 0x100, handle);
} }
@ -226,6 +282,20 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
ipcMessage.Thread.Reschedule(ThreadSchedState.Running); 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 GetProcessId64(int handle, out long pid) public KernelResult GetProcessId64(int handle, out long pid)
{ {
return GetProcessId(handle, out pid); return GetProcessId(handle, out pid);
@ -522,6 +592,139 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success; return KernelResult.Success;
} }
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;
}
KServerSession session = serverPort.IsLight
? serverPort.AcceptLightIncomingConnection()
: serverPort.AcceptIncomingConnection();
if (session != null)
{
currentProcess.HandleTable.SetReservedHandleObj(handle, session);
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 CreateEvent64(out int wEventHandle, out int rEventHandle) public KernelResult CreateEvent64(out int wEventHandle, out int rEventHandle)
{ {
return CreateEvent(out wEventHandle, out rEventHandle); return CreateEvent(out wEventHandle, out rEventHandle);
@ -749,7 +952,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
port.Initialize(maxSessions, false, 0); port.Initialize(maxSessions, false, 0);
result = port.SetName(name); result = port.ClientPort.SetName(name);
if (result != KernelResult.Success) if (result != KernelResult.Success)
{ {

View file

@ -63,6 +63,8 @@ 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) },
{ 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) },

View file

@ -62,22 +62,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
priority, priority,
cpuCore); cpuCore);
if (result != KernelResult.Success) if (result == KernelResult.Success)
{ {
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
return result;
}
result = _process.HandleTable.GenerateHandle(thread, out handle); result = _process.HandleTable.GenerateHandle(thread, out handle);
}
if (result != KernelResult.Success) else
{ {
thread.Terminate();
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1); currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
} }
thread.DecrementReferenceCount();
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

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

@ -131,7 +131,7 @@ namespace Ryujinx.HLE.HOS.Services
} }
else else
{ {
KSession session = new KSession(obj, context.Session.ServiceName); KSession session = new KSession(context.Device.System, obj, context.Session.ServiceName);
if (context.Process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success) if (context.Process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success)
{ {

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

@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
return 0; return 0;
} }
KSession session = new KSession(ServiceFactory.MakeService(context.Device.System, name), name); KSession session = new KSession(context.Device.System, ServiceFactory.MakeService(context.Device.System, name), name);
if (context.Process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success) if (context.Process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success)
{ {

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