Ryujinx/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs

828 lines
25 KiB
C#
Raw Normal View History

2018-02-04 18:08:20 -05:00
using ChocolArm64.Memory;
using ChocolArm64.State;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Services;
2018-02-04 18:08:20 -05:00
using System;
2018-02-19 14:37:13 -05:00
using System.Threading;
2018-02-04 18:08:20 -05:00
using static Ryujinx.HLE.HOS.ErrorCode;
namespace Ryujinx.HLE.HOS.Kernel
2018-02-04 18:08:20 -05:00
{
partial class SvcHandler
{
private void SvcExitProcess(CpuThreadState threadState)
{
_system.Scheduler.GetCurrentProcess().Terminate();
}
private void SignalEvent64(CpuThreadState threadState)
{
threadState.X0 = (ulong)SignalEvent((int)threadState.X0);
}
private KernelResult SignalEvent(int handle)
{
KWritableEvent writableEvent = _process.HandleTable.GetObject<KWritableEvent>(handle);
KernelResult result;
if (writableEvent != null)
{
writableEvent.Signal();
result = KernelResult.Success;
}
else
{
result = KernelResult.InvalidHandle;
}
if (result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + result + "!");
}
return result;
}
private void ClearEvent64(CpuThreadState threadState)
{
threadState.X0 = (ulong)ClearEvent((int)threadState.X0);
}
private KernelResult ClearEvent(int handle)
{
KernelResult result;
KWritableEvent writableEvent = _process.HandleTable.GetObject<KWritableEvent>(handle);
if (writableEvent == null)
{
KReadableEvent readableEvent = _process.HandleTable.GetObject<KReadableEvent>(handle);
result = readableEvent?.Clear() ?? KernelResult.InvalidHandle;
}
else
{
result = writableEvent.Clear();
}
if (result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + result + "!");
}
return result;
}
private void SvcCloseHandle(CpuThreadState threadState)
2018-02-04 18:08:20 -05:00
{
int handle = (int)threadState.X0;
2018-02-04 18:08:20 -05:00
object obj = _process.HandleTable.GetObject<object>(handle);
_process.HandleTable.CloseHandle(handle);
if (obj == null)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{handle:x8}!");
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
}
if (obj is KSession session)
{
session.Dispose();
}
else if (obj is KTransferMemory transferMemory)
{
_process.MemoryManager.ResetTransferMemory(
transferMemory.Address,
transferMemory.Size);
}
2018-02-04 18:08:20 -05:00
threadState.X0 = 0;
2018-02-04 18:08:20 -05:00
}
private void ResetSignal64(CpuThreadState threadState)
2018-02-04 18:08:20 -05:00
{
threadState.X0 = (ulong)ResetSignal((int)threadState.X0);
}
2018-02-04 18:08:20 -05:00
private KernelResult ResetSignal(int handle)
{
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KReadableEvent readableEvent = currentProcess.HandleTable.GetObject<KReadableEvent>(handle);
2018-02-04 18:08:20 -05:00
KernelResult result;
if (readableEvent != null)
{
result = readableEvent.ClearIfSignaled();
}
else
{
KProcess process = currentProcess.HandleTable.GetKProcess(handle);
if (process != null)
{
result = process.ClearIfNotExited();
}
else
{
result = KernelResult.InvalidHandle;
}
}
if (result == KernelResult.InvalidState)
{
Logger.PrintDebug(LogClass.KernelSvc, "Operation failed with error: " + result + "!");
}
else if (result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + result + "!");
}
return result;
2018-02-04 18:08:20 -05:00
}
private void SvcGetSystemTick(CpuThreadState threadState)
2018-02-04 18:08:20 -05:00
{
threadState.X0 = threadState.CntpctEl0;
2018-02-04 18:08:20 -05:00
}
private void SvcConnectToNamedPort(CpuThreadState threadState)
2018-02-04 18:08:20 -05:00
{
long stackPtr = (long)threadState.X0;
long namePtr = (long)threadState.X1;
2018-02-04 18:08:20 -05:00
string name = MemoryHelper.ReadAsciiString(_memory, namePtr, 8);
2018-02-04 18:08:20 -05:00
//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);
2018-02-04 18:08:20 -05:00
if (_process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
threadState.X0 = 0;
threadState.X1 = (uint)handle;
2018-02-04 18:08:20 -05:00
}
private void SvcSendSyncRequest(CpuThreadState threadState)
2018-02-04 18:08:20 -05:00
{
SendSyncRequest(threadState, threadState.Tpidr, 0x100, (int)threadState.X0);
2018-02-04 18:08:20 -05:00
}
private void SvcSendSyncRequestWithUserBuffer(CpuThreadState threadState)
2018-02-04 18:08:20 -05:00
{
SendSyncRequest(
threadState,
(long)threadState.X0,
(long)threadState.X1,
(int)threadState.X2);
2018-02-04 18:08:20 -05:00
}
private void SendSyncRequest(CpuThreadState threadState, long messagePtr, long size, int handle)
2018-02-04 18:08:20 -05:00
{
byte[] messageData = _memory.ReadBytes(messagePtr, size);
2018-02-04 18:08:20 -05:00
KSession session = _process.HandleTable.GetObject<KSession>(handle);
2018-02-19 14:37:13 -05:00
if (session != null)
{
_system.CriticalSection.Enter();
2018-02-19 14:37:13 -05:00
KThread currentThread = _system.Scheduler.GetCurrentThread();
currentThread.SignaledObj = null;
currentThread.ObjSyncResult = 0;
2018-02-04 18:08:20 -05:00
currentThread.Reschedule(ThreadSchedState.Paused);
2018-02-04 18:08:20 -05:00
IpcMessage message = new IpcMessage(messageData, messagePtr);
2018-02-04 18:08:20 -05:00
ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage(
currentThread,
session,
message,
messagePtr));
_system.ThreadCounter.AddCount();
_system.CriticalSection.Leave();
threadState.X0 = (ulong)currentThread.ObjSyncResult;
2018-02-04 18:08:20 -05:00
}
else
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!");
2018-02-19 14:37:13 -05:00
threadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
2018-02-04 18:08:20 -05:00
}
private void ProcessIpcRequest(object state)
{
HleIpcMessage ipcMessage = (HleIpcMessage)state;
ipcMessage.Thread.ObjSyncResult = (int)IpcHandler.IpcCall(
_device,
_process,
_memory,
ipcMessage.Session,
ipcMessage.Message,
ipcMessage.MessagePtr);
_system.ThreadCounter.Signal();
ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
}
private void GetProcessId64(CpuThreadState threadState)
{
int handle = (int)threadState.X1;
KernelResult result = GetProcessId(handle, out long pid);
threadState.X0 = (ulong)result;
threadState.X1 = (ulong)pid;
}
private KernelResult GetProcessId(int handle, out long pid)
{
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KProcess process = currentProcess.HandleTable.GetKProcess(handle);
if (process == null)
{
KThread thread = currentProcess.HandleTable.GetKThread(handle);
if (thread != null)
{
process = thread.Owner;
}
//TODO: KDebugEvent.
}
pid = process?.Pid ?? 0;
return process != null
? KernelResult.Success
: KernelResult.InvalidHandle;
}
private void SvcBreak(CpuThreadState threadState)
2018-02-04 18:08:20 -05:00
{
long reason = (long)threadState.X0;
long unknown = (long)threadState.X1;
long info = (long)threadState.X2;
2018-02-04 18:08:20 -05:00
KThread currentThread = _system.Scheduler.GetCurrentThread();
if ((reason & (1 << 31)) == 0)
{
currentThread.PrintGuestStackTrace();
throw new GuestBrokeExecutionException();
}
else
{
Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered.");
currentThread.PrintGuestStackTrace();
}
2018-02-04 18:08:20 -05:00
}
private void SvcOutputDebugString(CpuThreadState threadState)
2018-02-04 18:08:20 -05:00
{
long position = (long)threadState.X0;
long size = (long)threadState.X1;
2018-02-04 18:08:20 -05:00
string str = MemoryHelper.ReadAsciiString(_memory, position, size);
2018-02-04 18:08:20 -05:00
Logger.PrintWarning(LogClass.KernelSvc, str);
2018-02-04 18:08:20 -05:00
threadState.X0 = 0;
2018-02-04 18:08:20 -05:00
}
private void GetInfo64(CpuThreadState threadState)
2018-02-04 18:08:20 -05:00
{
long stackPtr = (long)threadState.X0;
uint id = (uint)threadState.X1;
int handle = (int)threadState.X2;
long subId = (long)threadState.X3;
2018-02-04 18:08:20 -05:00
KernelResult result = GetInfo(id, handle, subId, out long value);
threadState.X0 = (ulong)result;
threadState.X1 = (ulong)value;
}
private KernelResult GetInfo(uint id, int handle, long subId, out long value)
{
value = 0;
switch (id)
2018-02-04 18:08:20 -05:00
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 20:
case 21:
case 22:
{
if (subId != 0)
{
return KernelResult.InvalidCombination;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KProcess process = currentProcess.HandleTable.GetKProcess(handle);
if (process == null)
{
return KernelResult.InvalidHandle;
}
switch (id)
{
case 0: value = process.Capabilities.AllowedCpuCoresMask; break;
case 1: value = process.Capabilities.AllowedThreadPriosMask; break;
case 2: value = (long)process.MemoryManager.AliasRegionStart; break;
case 3: value = (long)(process.MemoryManager.AliasRegionEnd -
process.MemoryManager.AliasRegionStart); break;
case 4: value = (long)process.MemoryManager.HeapRegionStart; break;
case 5: value = (long)(process.MemoryManager.HeapRegionEnd -
process.MemoryManager.HeapRegionStart); break;
case 6: value = (long)process.GetMemoryCapacity(); break;
case 7: value = (long)process.GetMemoryUsage(); break;
case 12: value = (long)process.MemoryManager.GetAddrSpaceBaseAddr(); break;
case 13: value = (long)process.MemoryManager.GetAddrSpaceSize(); break;
case 14: value = (long)process.MemoryManager.StackRegionStart; break;
case 15: value = (long)(process.MemoryManager.StackRegionEnd -
process.MemoryManager.StackRegionStart); break;
case 16: value = (long)process.PersonalMmHeapPagesCount * KMemoryManager.PageSize; break;
case 17:
if (process.PersonalMmHeapPagesCount != 0)
{
value = process.MemoryManager.GetMmUsedPages() * KMemoryManager.PageSize;
}
break;
case 18: value = process.TitleId; break;
case 20: value = (long)process.UserExceptionContextAddress; break;
case 21: value = (long)process.GetMemoryCapacityWithoutPersonalMmHeap(); break;
case 22: value = (long)process.GetMemoryUsageWithoutPersonalMmHeap(); break;
}
break;
}
case 8:
{
if (handle != 0)
{
return KernelResult.InvalidHandle;
}
if (subId != 0)
{
return KernelResult.InvalidCombination;
}
value = _system.Scheduler.GetCurrentProcess().Debug ? 1 : 0;
break;
}
case 9:
{
if (handle != 0)
{
return KernelResult.InvalidHandle;
}
if (subId != 0)
{
return KernelResult.InvalidCombination;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
if (currentProcess.ResourceLimit != null)
{
KHandleTable handleTable = currentProcess.HandleTable;
KResourceLimit resourceLimit = currentProcess.ResourceLimit;
KernelResult result = handleTable.GenerateHandle(resourceLimit, out int resLimHandle);
if (result != KernelResult.Success)
{
return result;
}
value = (uint)resLimHandle;
}
break;
}
case 10:
{
if (handle != 0)
{
return KernelResult.InvalidHandle;
}
int currentCore = _system.Scheduler.GetCurrentThread().CurrentCore;
if (subId != -1 && subId != currentCore)
{
return KernelResult.InvalidCombination;
}
value = _system.Scheduler.CoreContexts[currentCore].TotalIdleTimeTicks;
break;
}
case 11:
{
if (handle != 0)
{
return KernelResult.InvalidHandle;
}
if ((ulong)subId > 3)
{
return KernelResult.InvalidCombination;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
value = currentProcess.RandomEntropy[subId];
break;
}
case 0xf0000002u:
{
if (subId < -1 || subId > 3)
{
return KernelResult.InvalidCombination;
}
KThread thread = _system.Scheduler.GetCurrentProcess().HandleTable.GetKThread(handle);
if (thread == null)
{
return KernelResult.InvalidHandle;
}
KThread currentThread = _system.Scheduler.GetCurrentThread();
int currentCore = currentThread.CurrentCore;
if (subId != -1 && subId != currentCore)
{
return KernelResult.Success;
}
2018-02-04 18:08:20 -05:00
KCoreContext coreContext = _system.Scheduler.CoreContexts[currentCore];
long timeDelta = PerformanceCounter.ElapsedMilliseconds - coreContext.LastContextSwitchTime;
if (subId != -1)
{
value = KTimeManager.ConvertMillisecondsToTicks(timeDelta);
}
else
{
long totalTimeRunning = thread.TotalTimeRunning;
if (thread == currentThread)
{
totalTimeRunning += timeDelta;
}
value = KTimeManager.ConvertMillisecondsToTicks(totalTimeRunning);
}
break;
}
default: return KernelResult.InvalidEnumValue;
2018-02-04 18:08:20 -05:00
}
return KernelResult.Success;
2018-02-04 18:08:20 -05:00
}
private void CreateEvent64(CpuThreadState state)
{
KernelResult result = CreateEvent(out int wEventHandle, out int rEventHandle);
state.X0 = (ulong)result;
state.X1 = (ulong)wEventHandle;
state.X2 = (ulong)rEventHandle;
}
private KernelResult CreateEvent(out int wEventHandle, out int rEventHandle)
{
KEvent Event = new KEvent(_system);
KernelResult result = _process.HandleTable.GenerateHandle(Event.WritableEvent, out wEventHandle);
if (result == KernelResult.Success)
{
result = _process.HandleTable.GenerateHandle(Event.ReadableEvent, out rEventHandle);
if (result != KernelResult.Success)
{
_process.HandleTable.CloseHandle(wEventHandle);
}
}
else
{
rEventHandle = 0;
}
return result;
}
private void GetProcessList64(CpuThreadState state)
{
ulong address = state.X1;
int maxOut = (int)state.X2;
KernelResult result = GetProcessList(address, maxOut, out int count);
state.X0 = (ulong)result;
state.X1 = (ulong)count;
}
private KernelResult GetProcessList(ulong address, int maxCount, out int count)
{
count = 0;
if ((maxCount >> 28) != 0)
{
return KernelResult.MaximumExceeded;
}
if (maxCount != 0)
{
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
ulong copySize = (ulong)maxCount * 8;
if (address + copySize <= address)
{
return KernelResult.InvalidMemState;
}
if (currentProcess.MemoryManager.OutsideAddrSpace(address, copySize))
{
return KernelResult.InvalidMemState;
}
}
int copyCount = 0;
lock (_system.Processes)
{
foreach (KProcess process in _system.Processes.Values)
{
if (copyCount < maxCount)
{
if (!KernelTransfer.KernelToUserInt64(_system, (long)address + copyCount * 8, process.Pid))
{
return KernelResult.UserCopyFailed;
}
}
copyCount++;
}
}
count = copyCount;
return KernelResult.Success;
}
private void GetSystemInfo64(CpuThreadState state)
{
uint id = (uint)state.X1;
int handle = (int)state.X2;
long subId = (long)state.X3;
KernelResult result = GetSystemInfo(id, handle, subId, out long value);
state.X0 = (ulong)result;
state.X1 = (ulong)value;
}
private KernelResult GetSystemInfo(uint id, int handle, long subId, out long value)
{
value = 0;
if (id > 2)
{
return KernelResult.InvalidEnumValue;
}
if (handle != 0)
{
return KernelResult.InvalidHandle;
}
if (id < 2)
{
if ((ulong)subId > 3)
{
return KernelResult.InvalidCombination;
}
KMemoryRegionManager region = _system.MemoryRegions[subId];
switch (id)
{
//Memory region capacity.
case 0: value = (long)region.Size; break;
//Memory region free space.
case 1:
{
ulong freePagesCount = region.GetFreePages();
value = (long)(freePagesCount * KMemoryManager.PageSize);
break;
}
}
}
else /* if (Id == 2) */
{
if ((ulong)subId > 1)
{
return KernelResult.InvalidCombination;
}
switch (subId)
{
case 0: value = _system.PrivilegedProcessLowestId; break;
case 1: value = _system.PrivilegedProcessHighestId; break;
}
}
return KernelResult.Success;
}
private void CreatePort64(CpuThreadState state)
{
int maxSessions = (int)state.X2;
bool isLight = (state.X3 & 1) != 0;
long nameAddress = (long)state.X4;
KernelResult result = CreatePort(
maxSessions,
isLight,
nameAddress,
out int serverPortHandle,
out int clientPortHandle);
state.X0 = (ulong)result;
state.X1 = (ulong)serverPortHandle;
state.X2 = (ulong)clientPortHandle;
}
private KernelResult CreatePort(
int maxSessions,
bool isLight,
long nameAddress,
out int serverPortHandle,
out int clientPortHandle)
{
serverPortHandle = clientPortHandle = 0;
if (maxSessions < 1)
{
return KernelResult.MaximumExceeded;
}
KPort port = new KPort(_system);
port.Initialize(maxSessions, isLight, nameAddress);
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;
}
private void ManageNamedPort64(CpuThreadState state)
{
long nameAddress = (long)state.X1;
int maxSessions = (int)state.X2;
KernelResult result = ManageNamedPort(nameAddress, maxSessions, out int handle);
state.X0 = (ulong)result;
state.X1 = (ulong)handle;
}
private KernelResult ManageNamedPort(long nameAddress, int maxSessions, out int handle)
{
handle = 0;
if (!KernelTransfer.UserToKernelString(_system, nameAddress, 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;
}
2018-02-04 18:08:20 -05:00
}
}