ryujinx/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs
gdkchan f77694e4f7
Implement a new physical memory manager and replace DeviceMemory (#856)
* Implement a new physical memory manager and replace DeviceMemory

* Proper generic constraints

* Fix debug build

* Add memory tests

* New CPU memory manager and general code cleanup

* Remove host memory management from CPU project, use Ryujinx.Memory instead

* Fix tests

* Document exceptions on MemoryBlock

* Fix leak on unix memory allocation

* Proper disposal of some objects on tests

* Fix JitCache not being set as initialized

* GetRef without checks for 8-bits and 16-bits CAS

* Add MemoryBlock destructor

* Throw in separate method to improve codegen

* Address PR feedback

* QueryModified improvements

* Fix memory write tracking not marking all pages as modified in some cases

* Simplify MarkRegionAsModified

* Remove XML doc for ghost param

* Add back optimization to avoid useless buffer updates

* Add Ryujinx.Cpu project, move MemoryManager there and remove MemoryBlockWrapper

* Some nits

* Do not perform address translation when size is 0

* Address PR feedback and format NativeInterface class

* Remove ghost parameter description

* Update Ryujinx.Cpu to .NET Core 3.1

* Address PR feedback

* Fix build

* Return a well defined value for GetPhysicalAddress with invalid VA, and do not return unmapped ranges as modified

* Typo
2020-05-04 08:54:50 +10:00

608 lines
19 KiB
C#

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([R(1)] ulong namePtr, [R(1)] out int handle)
{
return ConnectToNamedPort(namePtr, out handle);
}
public KernelResult ConnectToNamedPort32([R(1)] uint namePtr, [R(1)] 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([R(0)] int handle)
{
return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle);
}
public KernelResult SendSyncRequest32([R(0)] int handle)
{
return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle);
}
public KernelResult SendSyncRequestWithUserBuffer64([R(0)] ulong messagePtr, [R(1)] ulong size, [R(2)] int handle)
{
return SendSyncRequest(messagePtr, size, handle);
}
public KernelResult SendSyncRequestWithUserBuffer32([R(0)] uint messagePtr, [R(1)] uint size, [R(2)] int handle)
{
return SendSyncRequest(messagePtr, size, handle);
}
private KernelResult SendSyncRequest(ulong messagePtr, ulong size, int handle)
{
byte[] messageData = new byte[size];
_process.CpuMemory.Read(messagePtr, messageData);
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,
_process.CpuMemory,
ipcMessage.Thread,
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(
[R(2)] bool isLight,
[R(3)] ulong namePtr,
[R(1)] out int serverSessionHandle,
[R(2)] out int clientSessionHandle)
{
return CreateSession(isLight, namePtr, out serverSessionHandle, out clientSessionHandle);
}
public KernelResult CreateSession32(
[R(2)] bool isLight,
[R(3)] uint namePtr,
[R(1)] out int serverSessionHandle,
[R(2)] 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([R(1)] int portHandle, [R(1)] out int sessionHandle)
{
return AcceptSession(portHandle, out sessionHandle);
}
public KernelResult AcceptSession32([R(1)] int portHandle, [R(1)] 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(
[R(1)] ulong handlesPtr,
[R(2)] int handlesCount,
[R(3)] int replyTargetHandle,
[R(4)] long timeout,
[R(1)] out int handleIndex)
{
return ReplyAndReceive(handlesPtr, handlesCount, replyTargetHandle, timeout, out handleIndex);
}
public KernelResult ReplyAndReceive32(
[R(0)] uint timeoutLow,
[R(1)] ulong handlesPtr,
[R(2)] int handlesCount,
[R(3)] int replyTargetHandle,
[R(4)] uint timeoutHigh,
[R(1)] out int handleIndex)
{
long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32));
return ReplyAndReceive(handlesPtr, handlesCount, replyTargetHandle, timeout, out handleIndex);
}
public KernelResult ReplyAndReceive(
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(
[R(2)] int maxSessions,
[R(3)] bool isLight,
[R(4)] ulong namePtr,
[R(1)] out int serverPortHandle,
[R(2)] out int clientPortHandle)
{
return CreatePort(maxSessions, isLight, namePtr, out serverPortHandle, out clientPortHandle);
}
public KernelResult CreatePort32(
[R(0)] uint namePtr,
[R(2)] int maxSessions,
[R(3)] bool isLight,
[R(1)] out int serverPortHandle,
[R(2)] 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([R(1)] ulong namePtr, [R(2)] int maxSessions, [R(1)] out int handle)
{
return ManageNamedPort(namePtr, maxSessions, out handle);
}
public KernelResult ManageNamedPort32([R(1)] uint namePtr, [R(2)] int maxSessions, [R(1)] 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([R(1)] int clientPortHandle, [R(1)] out int clientSessionHandle)
{
return ConnectToPort(clientPortHandle, out clientSessionHandle);
}
public KernelResult ConnectToPort32([R(1)] int clientPortHandle, [R(1)] 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;
}
}
}