0
0
Fork 0
mirror of https://github.com/ryujinx-mirror/ryujinx.git synced 2024-12-24 03:05:45 +00:00
ryujinx-fork/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs
Thog 55c956e2ec Make HLE disposable safely (#850)
* Make HLE disposable safely

This fix the oldest issue with the HLE code: the kernel side
disposability.

Changelog:

- Implement KProcess::UnpauseAndTerminateAllThreadsExcept, KThread::Terminate, KThread::TerminateCurrentProcess, KThread::PrepareForTermiation and the svc post handler accurately.
- Implement svcTerminateProcess and svcExitProcess. (both untested)
- Fix KHandleTable::Destroy not decrementing refcount of all objects stored in the table.
- Spawn a custom KProcess with the maximum priority to terminate every guest KProcess. (terminating kernel emulation safely)
- General system stability improvements to enhance the user's experience.

* Fix a typo in a comment in KProcess.cs

* Address gdk's comments
2019-12-26 02:50:17 +01:00

624 lines
18 KiB
C#

using ARMeilleure.Memory;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Ipc;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
partial class SvcHandler
{
public void ExitProcess64()
{
ExitProcess();
}
public KernelResult TerminateProcess64(int handle)
{
return TerminateProcess(handle);
}
private KernelResult TerminateProcess(int handle)
{
KProcess process = _process.HandleTable.GetObject<KProcess>(handle);
KernelResult result;
if (process != null)
{
if (process == _system.Scheduler.GetCurrentProcess())
{
result = KernelResult.Success;
process.DecrementToZeroWhileTerminatingCurrent();
}
else
{
result = process.Terminate();
process.DecrementReferenceCount();
}
}
else
{
result = KernelResult.InvalidHandle;
}
return result;
}
private void ExitProcess()
{
_system.Scheduler.GetCurrentProcess().TerminateCurrentProcess();
}
public KernelResult SignalEvent64(int handle)
{
return SignalEvent(handle);
}
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;
}
return result;
}
public KernelResult ClearEvent64(int handle)
{
return ClearEvent(handle);
}
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();
}
return result;
}
public KernelResult CloseHandle64(int handle)
{
return CloseHandle(handle);
}
private KernelResult CloseHandle(int handle)
{
KAutoObject obj = _process.HandleTable.GetObject<KAutoObject>(handle);
_process.HandleTable.CloseHandle(handle);
if (obj == null)
{
return KernelResult.InvalidHandle;
}
if (obj is KSession session)
{
session.Dispose();
}
else if (obj is KTransferMemory transferMemory)
{
_process.MemoryManager.ResetTransferMemory(
transferMemory.Address,
transferMemory.Size);
}
return KernelResult.Success;
}
public KernelResult ResetSignal64(int handle)
{
return ResetSignal(handle);
}
private KernelResult ResetSignal(int handle)
{
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KReadableEvent readableEvent = currentProcess.HandleTable.GetObject<KReadableEvent>(handle);
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;
}
}
return result;
}
public ulong GetSystemTick64()
{
return _system.Scheduler.GetCurrentThread().Context.CntpctEl0;
}
public KernelResult GetProcessId64(int handle, out long pid)
{
return GetProcessId(handle, out 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;
}
public void Break64(ulong reason, ulong x1, ulong info)
{
Break(reason);
}
private void Break(ulong reason)
{
KThread currentThread = _system.Scheduler.GetCurrentThread();
if ((reason & (1UL << 31)) == 0)
{
currentThread.PrintGuestStackTrace();
// As the process is exiting, this is probably caused by emulation termination.
if (currentThread.Owner.State == ProcessState.Exiting)
{
return;
}
// TODO: Debug events.
currentThread.Owner.TerminateCurrentProcess();
throw new GuestBrokeExecutionException();
}
else
{
Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered.");
currentThread.PrintGuestStackTrace();
}
}
public void OutputDebugString64(ulong strPtr, ulong size)
{
OutputDebugString(strPtr, size);
}
private void OutputDebugString(ulong strPtr, ulong size)
{
string str = MemoryHelper.ReadAsciiString(_process.CpuMemory, (long)strPtr, (long)size);
Logger.PrintWarning(LogClass.KernelSvc, str);
}
public KernelResult GetInfo64(uint id, int handle, long subId, out long value)
{
return GetInfo(id, handle, subId, out value);
}
private KernelResult GetInfo(uint id, int handle, long subId, out long value)
{
value = 0;
switch (id)
{
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 = (long)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;
}
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;
}
return KernelResult.Success;
}
public KernelResult CreateEvent64(out int wEventHandle, out int rEventHandle)
{
return CreateEvent(out wEventHandle, out 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;
}
public KernelResult GetProcessList64(ulong address, int maxCount, out int count)
{
return GetProcessList(address, maxCount, out 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, address + (ulong)copyCount * 8, process.Pid))
{
return KernelResult.UserCopyFailed;
}
}
copyCount++;
}
}
count = copyCount;
return KernelResult.Success;
}
public KernelResult GetSystemInfo64(uint id, int handle, long subId, out long value)
{
return GetSystemInfo(id, handle, subId, out 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;
}
}
}