Misc fixes and improvements on new IPC methods

This commit is contained in:
gdkchan 2018-12-29 03:13:04 -03:00
parent 357c0c7977
commit 89e703302b
12 changed files with 239 additions and 156 deletions

View file

@ -115,6 +115,16 @@ 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;
@ -132,6 +142,21 @@ namespace Ryujinx.HLE
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)

View file

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

View file

@ -18,15 +18,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
//SM implementation to work with the new IPC system.
public IpcService Service { get; set; }
public KClientPort(Horizon system) : base(system)
{
_countIncLock = new object();
}
public void Initialize(KPort parent, int maxSessions)
public KClientPort(Horizon system, KPort parent, int maxSessions) : base(system)
{
_maxSessions = maxSessions;
_parent = parent;
_countIncLock = new object();
}
public KernelResult Connect(out KClientSession clientSession)

View file

@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
private KSession _parent;
public int ResourceStatus { get; private set; }
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.
@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
_parent = parent;
ResourceStatus = 1;
State = ChannelState.Open;
}
public KernelResult SendSyncRequest(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
@ -51,6 +51,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
protected override void Destroy()
{
_parent.DisconnectClient();
_parent.DecrementReferenceCount();
}
}

View file

@ -8,25 +8,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KClientPort ClientPort { get; }
private long _nameAddress;
private int _resourceStatus;
private ChannelState _state;
public bool IsLight { get; private set; }
public KPort(Horizon system) : base(system)
public KPort(Horizon system, int maxSessions, bool isLight, long nameAddress) : base(system)
{
ServerPort = new KServerPort(system);
ClientPort = new KClientPort(system);
}
public void Initialize(int maxSessions, bool isLight, long nameAddress)
{
ServerPort.Initialize(this);
ClientPort.Initialize(this, maxSessions);
ServerPort = new KServerPort(system, this);
ClientPort = new KClientPort(system, this, maxSessions);
IsLight = isLight;
_nameAddress = nameAddress;
_resourceStatus = 1;
_state = ChannelState.Open;
}
public KernelResult EnqueueIncomingSession(KServerSession session)
@ -35,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
System.CriticalSection.Enter();
if (_resourceStatus == 1)
if (_state == ChannelState.Open)
{
ServerPort.EnqueueIncomingSession(session);

View file

@ -12,15 +12,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
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 KServerPort(Horizon system, KPort parent) : base(system)
{
_parent = parent;
_incomingConnections = new LinkedList<KServerSession>();
_lightIncomingConnections = new LinkedList<KServerSession>();
}
public void EnqueueIncomingSession(KServerSession session)

View file

@ -173,20 +173,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KernelResult EnqueueRequest(KSessionRequest request)
{
if (_parent.ClientSession.ResourceStatus != 1)
if (_parent.ClientSession.State != ChannelState.Open)
{
return KernelResult.PortRemoteClosed;
}
if (request.AsyncEvent == null)
{
if (request.SenderThread.ShallBeTerminated ||
request.SenderThread.SchedFlags == ThreadSchedState.TerminationPending)
if (request.ClientThread.ShallBeTerminated ||
request.ClientThread.SchedFlags == ThreadSchedState.TerminationPending)
{
return KernelResult.ThreadTerminating;
}
request.SenderThread.Reschedule(ThreadSchedState.Paused);
request.ClientThread.Reschedule(ThreadSchedState.Paused);
}
_requests.AddLast(request);
@ -206,38 +206,36 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
System.CriticalSection.Enter();
if (_parent.ClientSession.ResourceStatus != 1)
if (_parent.ClientSession.State != ChannelState.Open)
{
System.CriticalSection.Leave();
return KernelResult.PortRemoteClosed;
}
if (_activeRequest != null || _requests.Count == 0)
if (_activeRequest != null || !PickRequest(out KSessionRequest request))
{
System.CriticalSection.Leave();
return KernelResult.NotFound;
}
KSessionRequest request = _requests.First.Value;
_requests.RemoveFirst();
if (request.SenderThread == null)
if (request.ClientThread == null)
{
System.CriticalSection.Leave();
return KernelResult.PortRemoteClosed;
}
KThread clientThread = request.SenderThread;
KThread clientThread = request.ClientThread;
KProcess clientProcess = clientThread.Owner;
System.CriticalSection.Leave();
_activeRequest = request;
request.ServerProcess = serverProcess;
Message clientMsg = new Message(
clientThread,
request.CustomCmdBuffAddr,
@ -259,7 +257,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
void CleanUpForError()
{
request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
if (request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager) == KernelResult.Success)
{
request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager);
}
CloseAllHandles(serverMsg, header, serverProcess);
@ -274,29 +275,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
System.CriticalSection.Leave();
if (request.AsyncEvent != null)
{
System.Device.Memory.WriteInt64((long)clientMsg.DramAddress + 0, 0);
System.Device.Memory.WriteInt32((long)clientMsg.DramAddress + 8, (int)clientResult);
clientProcess.MemoryManager.UnborrowIpcBuffer(clientMsg.Address, clientMsg.Size);
request.AsyncEvent.Signal();
}
else
{
System.CriticalSection.Enter();
if ((clientThread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
{
clientThread.SignaledObj = null;
clientThread.ObjSyncResult = clientResult;
clientThread.Reschedule(ThreadSchedState.Running);
}
System.CriticalSection.Leave();
}
WakeClient(request, clientResult);
}
if (header.ReceiveListType < 2 && header.ReceiveListOffset > clientMsg.Size)
@ -386,7 +365,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
if (clientResult == KernelResult.Success && handle != 0)
{
clientResult = GetCopyObjectHandle(clientThread, handle, out newHandle);
clientResult = GetCopyObjectHandle(clientThread, serverProcess, handle, out newHandle);
}
serverProcess.CpuMemory.WriteInt32((long)serverMsg.Address + offset * 4, newHandle);
@ -404,7 +383,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
if (clientResult == KernelResult.Success)
{
clientResult = GetMoveObjectHandle(serverProcess, handle, out newHandle);
clientResult = GetMoveObjectHandle(clientProcess, serverProcess, handle, out newHandle);
}
else
{
@ -640,7 +619,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
System.CriticalSection.Leave();
KThread clientThread = request.SenderThread;
KThread clientThread = request.ClientThread;
KProcess clientProcess = clientThread.Owner;
Message clientMsg = new Message(
@ -662,46 +641,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
KernelResult clientResult = KernelResult.Success;
KernelResult serverResult = KernelResult.Success;
void SendResultToClient()
{
if (request.AsyncEvent != null)
{
if (clientResult != KernelResult.Success)
{
System.Device.Memory.WriteInt64((long)clientMsg.DramAddress + 0, 0);
System.Device.Memory.WriteInt32((long)clientMsg.DramAddress + 8, (int)clientResult);
}
clientProcess.MemoryManager.UnborrowIpcBuffer(clientMsg.Address, clientMsg.Size);
request.AsyncEvent.Signal();
}
else
{
System.CriticalSection.Enter();
if ((clientThread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
{
clientThread.SignaledObj = null;
clientThread.ObjSyncResult = clientResult;
clientThread.Reschedule(ThreadSchedState.Running);
}
System.CriticalSection.Leave();
}
}
void CleanUpForError()
{
CloseAllHandles(clientMsg, header, serverProcess);
CloseAllHandles(clientMsg, header, clientProcess);
if (request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager) == KernelResult.Success)
{
request.BufferDescriptorTable.RestoreClientBuffers(serverProcess.MemoryManager);
}
SendResultToClient();
CancelRequest(request, clientResult);
}
if (header.ReceiveListType < 2 && header.ReceiveListOffset > clientMsg.Size)
@ -737,18 +681,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return KernelResult.CmdBufferTooSmall;
}
//Close move handles.
uint pidSizeInWords = header.HasPid ? 2u : 0u;
ulong moveHandlesAddr = serverMsg.Address + (3u + pidSizeInWords + header.CopyHandlesCount) * 4;
for (int index = 0; index < header.MoveHandlesCount; index++)
{
int handle = serverProcess.CpuMemory.ReadInt32((long)moveHandlesAddr + index * 4);
serverProcess.HandleTable.CloseHandle(handle);
}
//Read receive list.
ulong[] receiveList = null;
@ -806,10 +738,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
if (handle != 0)
{
GetCopyObjectHandle(clientThread, handle, out newHandle);
GetCopyObjectHandle(serverThread, clientProcess, handle, out newHandle);
}
serverProcess.CpuMemory.WriteInt32((long)serverMsg.Address + offset * 4, newHandle);
System.Device.Memory.WriteInt32((long)clientMsg.DramAddress + offset * 4, newHandle);
offset++;
}
@ -824,7 +756,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
if (clientResult == KernelResult.Success)
{
clientResult = GetMoveObjectHandle(serverProcess, handle, out newHandle);
clientResult = GetMoveObjectHandle(serverProcess, clientProcess, handle, out newHandle);
}
else
{
@ -832,7 +764,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
}
}
serverProcess.CpuMemory.WriteInt32((long)serverMsg.Address + offset * 4, newHandle);
System.Device.Memory.WriteInt32((long)clientMsg.DramAddress + offset * 4, newHandle);
offset++;
}
@ -941,36 +873,39 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return serverResult;
}
//Wake client thread.
SendResultToClient();
WakeClient(request, clientResult);
return serverResult;
}
private KernelResult GetCopyObjectHandle(KThread thread, int srcHandle, out int dstHandle)
private KernelResult GetCopyObjectHandle(
KThread srcThread,
KProcess dstProcess,
int srcHandle,
out int dstHandle)
{
dstHandle = 0;
KProcess process = thread.Owner;
KProcess srcProcess = srcThread.Owner;
KAutoObject obj;
if (srcHandle == KHandleTable.SelfProcessHandle)
{
obj = process;
obj = srcProcess;
}
else if (srcHandle == KHandleTable.SelfThreadHandle)
{
obj = thread;
obj = srcThread;
}
else
{
obj = process.HandleTable.GetObject<KAutoObject>(srcHandle);
obj = srcProcess.HandleTable.GetObject<KAutoObject>(srcHandle);
}
if (obj != null)
{
return process.HandleTable.GenerateHandle(obj, out dstHandle);
return dstProcess.HandleTable.GenerateHandle(obj, out dstHandle);
}
else
{
@ -978,7 +913,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
}
}
private KernelResult GetMoveObjectHandle(KProcess srcProcess, int srcHandle, out int dstHandle)
private KernelResult GetMoveObjectHandle(
KProcess srcProcess,
KProcess dstProcess,
int srcHandle,
out int dstHandle)
{
dstHandle = 0;
@ -986,7 +925,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
if (obj != null)
{
KernelResult result = srcProcess.HandleTable.GenerateHandle(obj, out dstHandle);
KernelResult result = dstProcess.HandleTable.GenerateHandle(obj, out dstHandle);
srcProcess.HandleTable.CloseHandle(srcHandle);
@ -1102,7 +1041,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public override bool IsSignaled()
{
if (_parent.ClientSession.ResourceStatus != 1)
if (_parent.ClientSession.State != ChannelState.Open)
{
return true;
}
@ -1112,7 +1051,110 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
protected override void Destroy()
{
_parent.DisconnectServer();
CancelAllRequests(KernelResult.PortRemoteClosed);
_parent.DecrementReferenceCount();
}
private void CancelAllRequests(KernelResult result)
{
System.CriticalSection.Enter();
if (_activeRequest != null)
{
KSessionRequest request = _activeRequest;
_activeRequest = null;
CancelRequest(request, result);
System.CriticalSection.Leave();
}
else
{
System.CriticalSection.Leave();
}
while (PickRequest(out KSessionRequest request))
{
CancelRequest(request, result);
}
}
private bool PickRequest(out KSessionRequest request)
{
request = null;
System.CriticalSection.Enter();
bool hasRequest = _requests.First != null;
if (hasRequest)
{
request = _requests.First.Value;
_requests.RemoveFirst();
}
System.CriticalSection.Leave();
return hasRequest;
}
private void CancelRequest(KSessionRequest request, KernelResult result)
{
KProcess clientProcess = request.ClientThread.Owner;
KProcess serverProcess = request.ServerProcess;
KernelResult unmapResult = KernelResult.Success;
if (serverProcess != null)
{
unmapResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
}
if (unmapResult == KernelResult.Success)
{
request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager);
}
WakeClient(request, result);
}
private void WakeClient(KSessionRequest request, KernelResult result)
{
KThread clientThread = request.ClientThread;
KProcess clientProcess = clientThread.Owner;
if (request.AsyncEvent != null)
{
ulong address = clientProcess.MemoryManager.GetDramAddressFromVa(request.CustomCmdBuffAddr);
System.Device.Memory.WriteInt64((long)address + 0, 0);
System.Device.Memory.WriteInt32((long)address + 8, (int)result);
clientProcess.MemoryManager.UnborrowIpcBuffer(
request.CustomCmdBuffAddr,
request.CustomCmdBuffSize);
request.AsyncEvent.Signal();
}
else
{
System.CriticalSection.Enter();
if ((clientThread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
{
clientThread.SignaledObj = null;
clientThread.ObjSyncResult = result;
clientThread.Reschedule(ThreadSchedState.Running);
}
System.CriticalSection.Leave();
}
}
}
}

View file

@ -17,6 +17,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
ClientSession = new KClientSession(system, this);
}
public void DisconnectClient()
{
if (ClientSession.State == ChannelState.Open)
{
ClientSession.State = ChannelState.ClientDisconnected;
//TODO: Wake up client, etc.
}
}
public void DisconnectServer()
{
if (ClientSession.State == ChannelState.Open)
{
ClientSession.State = ChannelState.ServerDisconnected;
}
}
public void Dispose()
{
Dispose(true);

View file

@ -1,3 +1,4 @@
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Ipc
@ -6,7 +7,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
public KBufferDescriptorTable BufferDescriptorTable { get; }
public KThread SenderThread { get; }
public KThread ClientThread { get; }
public KProcess ServerProcess { get; set; }
public KWritableEvent AsyncEvent { get; }
@ -14,11 +17,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public ulong CustomCmdBuffSize { get; }
public KSessionRequest(
KThread senderThread,
KThread clientThread,
ulong customCmdBuffAddr,
ulong customCmdBuffSize)
{
SenderThread = senderThread;
ClientThread = clientThread;
CustomCmdBuffAddr = customCmdBuffAddr;
CustomCmdBuffSize = customCmdBuffSize;

View file

@ -1778,7 +1778,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
if (visitedSize != 0)
{
InsertBlock(address, size, SetIpcMappingPermissions, permissionMask);
InsertBlock(address, visitedSize / PageSize, SetIpcMappingPermissions, permissionMask);
}
}
@ -1850,14 +1850,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
firstPageFillAddress += unusedSizeBefore + copySize;
unusedSizeAfter = addressRounded - endAddr;
unusedSizeAfter = addressRounded > endAddr ? addressRounded - endAddr : 0;
}
else
{
unusedSizeAfter = PageSize;
}
_system.Device.Memory.Set(firstPageFillAddress, 0, unusedSizeAfter);
if (unusedSizeAfter != 0)
{
_system.Device.Memory.Set(firstPageFillAddress, 0, unusedSizeAfter);
}
KPageList pages = new KPageList();
@ -1989,15 +1992,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
if (pageList.Nodes.Count != 0)
{
if (MapPages(va, pageList, permission) != KernelResult.Success)
KernelResult result = MapPages(va, pageList, permission);
if (result != KernelResult.Success)
{
throw new InvalidOperationException("Unexpected failure while trying to map pages.");
return result;
}
}
else
{
InsertBlock(va, neededPagesCount, state, permission);
}
InsertBlock(va, neededPagesCount, state, permission);
mappedVa = va;
}

View file

@ -666,6 +666,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
currentProcess.HandleTable.SetReservedHandleObj(handle, session);
session.DecrementReferenceCount();
sessionHandle = handle;
result = KernelResult.Success;
@ -933,9 +935,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.MaximumExceeded;
}
KPort port = new KPort(_system);
port.Initialize(maxSessions, isLight, (long)namePtr);
KPort port = new KPort(_system, maxSessions, isLight, (long)namePtr);
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
@ -980,7 +980,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KClientPort.RemoveName(_system, name);
}
KPort port = new KPort(_system);
KPort port = new KPort(_system, maxSessions, false, 0);
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
@ -991,8 +991,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
port.Initialize(maxSessions, false, 0);
result = port.ClientPort.SetName(name);
if (result != KernelResult.Success)

View file

@ -32,9 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
public static void InitializePort(Horizon system)
{
KPort port = new KPort(system);
port.Initialize(256, false, 0);
KPort port = new KPort(system, 256, false, 0);
port.ClientPort.SetName("sm:");
@ -110,11 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
return ErrorCode.MakeError(ErrorModule.Sm, SmErr.InvalidName);
}
System.Console.WriteLine("register service " + name + " " + maxSessions);
KPort port = new KPort(context.Device.System);
port.Initialize(maxSessions, isLight, 0);
KPort port = new KPort(context.Device.System, maxSessions, isLight, 0);
if (!_registeredServices.TryAdd(name, port))
{