Ryujinx/Ryujinx.HLE/HOS/Kernel/KProcess.cs

1013 lines
30 KiB
C#
Raw Normal View History

using ChocolArm64;
using ChocolArm64.Events;
using ChocolArm64.Memory;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel
{
class KProcess : KSynchronizationObject
{
public const int KernelVersionMajor = 10;
public const int KernelVersionMinor = 4;
public const int KernelVersionRevision = 0;
public const int KernelVersionPacked =
(KernelVersionMajor << 19) |
(KernelVersionMinor << 15) |
(KernelVersionRevision << 0);
public KMemoryManager MemoryManager { get; private set; }
2018-12-01 21:01:59 +01:00
private SortedDictionary<ulong, KTlsPageInfo> _fullTlsPages;
private SortedDictionary<ulong, KTlsPageInfo> _freeTlsPages;
public int DefaultCpuCore { get; private set; }
public bool Debug { get; private set; }
public KResourceLimit ResourceLimit { get; private set; }
public ulong PersonalMmHeapPagesCount { get; private set; }
2018-12-01 21:01:59 +01:00
private ProcessState _state;
2018-12-01 21:01:59 +01:00
private object _processLock;
private object _threadingLock;
public KAddressArbiter AddressArbiter { get; private set; }
public long[] RandomEntropy { get; private set; }
2018-12-01 21:01:59 +01:00
private bool _signaled;
private bool _useSystemMemBlocks;
public string Name { get; private set; }
2018-12-01 21:01:59 +01:00
private int _threadCount;
public int MmuFlags { get; private set; }
2018-12-01 21:01:59 +01:00
private MemoryRegion _memRegion;
public KProcessCapabilities Capabilities { get; private set; }
public long TitleId { get; private set; }
public long Pid { get; private set; }
2018-12-01 21:01:59 +01:00
private long _creationTimestamp;
private ulong _entrypoint;
private ulong _imageSize;
private ulong _mainThreadStackSize;
private ulong _memoryUsageCapacity;
private int _category;
public KHandleTable HandleTable { get; private set; }
public ulong UserExceptionContextAddress { get; private set; }
2018-12-01 21:01:59 +01:00
private LinkedList<KThread> _threads;
public bool IsPaused { get; private set; }
public Translator Translator { get; private set; }
public MemoryManager CpuMemory { get; private set; }
2018-12-01 21:01:59 +01:00
private SvcHandler _svcHandler;
public HleProcessDebugger Debugger { get; private set; }
2018-12-01 21:01:59 +01:00
public KProcess(Horizon system) : base(system)
{
2018-12-01 21:01:59 +01:00
_processLock = new object();
_threadingLock = new object();
2018-12-01 21:01:59 +01:00
CpuMemory = new MemoryManager(system.Device.Memory.RamPointer);
CpuMemory.InvalidAccess += InvalidAccessHandler;
2018-12-01 21:01:59 +01:00
AddressArbiter = new KAddressArbiter(system);
2018-12-01 21:01:59 +01:00
MemoryManager = new KMemoryManager(system, CpuMemory);
2018-12-01 21:01:59 +01:00
_fullTlsPages = new SortedDictionary<ulong, KTlsPageInfo>();
_freeTlsPages = new SortedDictionary<ulong, KTlsPageInfo>();
Capabilities = new KProcessCapabilities();
RandomEntropy = new long[KScheduler.CpuCoresCount];
2018-12-01 21:01:59 +01:00
_threads = new LinkedList<KThread>();
Translator = new Translator();
Translator.CpuTrace += CpuTraceHandler;
2018-12-01 21:01:59 +01:00
_svcHandler = new SvcHandler(system.Device, this);
Debugger = new HleProcessDebugger(this);
}
public KernelResult InitializeKip(
2018-12-01 21:01:59 +01:00
ProcessCreationInfo creationInfo,
int[] caps,
KPageList pageList,
KResourceLimit resourceLimit,
MemoryRegion memRegion)
{
2018-12-01 21:24:37 +01:00
ResourceLimit = resourceLimit;
_memRegion = memRegion;
2018-12-01 21:01:59 +01:00
AddressSpaceType addrSpaceType = (AddressSpaceType)((creationInfo.MmuFlags >> 1) & 7);
2018-12-01 21:01:59 +01:00
bool aslrEnabled = ((creationInfo.MmuFlags >> 5) & 1) != 0;
2018-12-01 21:01:59 +01:00
ulong codeAddress = creationInfo.CodeAddress;
2018-12-01 21:01:59 +01:00
ulong codeSize = (ulong)creationInfo.CodePagesCount * KMemoryManager.PageSize;
2018-12-01 21:01:59 +01:00
KMemoryBlockAllocator memoryBlockAllocator = (MmuFlags & 0x40) != 0
? System.LargeMemoryBlockAllocator
: System.SmallMemoryBlockAllocator;
2018-12-01 21:01:59 +01:00
KernelResult result = MemoryManager.InitializeForProcess(
addrSpaceType,
aslrEnabled,
!aslrEnabled,
memRegion,
codeAddress,
codeSize,
memoryBlockAllocator);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
if (!ValidateCodeAddressAndSize(codeAddress, codeSize))
{
return KernelResult.InvalidMemRange;
}
2018-12-01 21:01:59 +01:00
result = MemoryManager.MapPages(
codeAddress,
pageList,
MemoryState.CodeStatic,
MemoryPermission.None);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
result = Capabilities.InitializeForKernel(caps, MemoryManager);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
2018-12-01 21:01:59 +01:00
return result;
}
Pid = System.GetKipId();
if (Pid == 0 || (ulong)Pid >= Horizon.InitialProcessId)
{
throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
}
2018-12-01 21:01:59 +01:00
result = ParseProcessInfo(creationInfo);
2018-12-01 21:01:59 +01:00
return result;
}
public KernelResult Initialize(
2018-12-01 21:01:59 +01:00
ProcessCreationInfo creationInfo,
int[] caps,
KResourceLimit resourceLimit,
MemoryRegion memRegion)
{
2018-12-01 21:24:37 +01:00
ResourceLimit = resourceLimit;
_memRegion = memRegion;
2018-12-01 21:01:59 +01:00
ulong personalMmHeapSize = GetPersonalMmHeapSize((ulong)creationInfo.PersonalMmHeapPagesCount, memRegion);
2018-12-01 21:01:59 +01:00
ulong codePagesCount = (ulong)creationInfo.CodePagesCount;
2018-12-01 21:01:59 +01:00
ulong neededSizeForProcess = personalMmHeapSize + codePagesCount * KMemoryManager.PageSize;
2018-12-01 21:01:59 +01:00
if (neededSizeForProcess != 0 && resourceLimit != null)
{
2018-12-01 21:01:59 +01:00
if (!resourceLimit.Reserve(LimitableResource.Memory, neededSizeForProcess))
{
return KernelResult.ResLimitExceeded;
}
}
void CleanUpForError()
{
2018-12-01 21:01:59 +01:00
if (neededSizeForProcess != 0 && resourceLimit != null)
{
2018-12-01 21:01:59 +01:00
resourceLimit.Release(LimitableResource.Memory, neededSizeForProcess);
}
}
2018-12-01 21:01:59 +01:00
PersonalMmHeapPagesCount = (ulong)creationInfo.PersonalMmHeapPagesCount;
2018-12-01 21:01:59 +01:00
KMemoryBlockAllocator memoryBlockAllocator;
if (PersonalMmHeapPagesCount != 0)
{
2018-12-01 21:01:59 +01:00
memoryBlockAllocator = new KMemoryBlockAllocator(PersonalMmHeapPagesCount * KMemoryManager.PageSize);
}
else
{
2018-12-01 21:01:59 +01:00
memoryBlockAllocator = (MmuFlags & 0x40) != 0
? System.LargeMemoryBlockAllocator
: System.SmallMemoryBlockAllocator;
}
2018-12-01 21:01:59 +01:00
AddressSpaceType addrSpaceType = (AddressSpaceType)((creationInfo.MmuFlags >> 1) & 7);
2018-12-01 21:01:59 +01:00
bool aslrEnabled = ((creationInfo.MmuFlags >> 5) & 1) != 0;
2018-12-01 21:01:59 +01:00
ulong codeAddress = creationInfo.CodeAddress;
2018-12-01 21:01:59 +01:00
ulong codeSize = codePagesCount * KMemoryManager.PageSize;
2018-12-01 21:01:59 +01:00
KernelResult result = MemoryManager.InitializeForProcess(
addrSpaceType,
aslrEnabled,
!aslrEnabled,
memRegion,
codeAddress,
codeSize,
memoryBlockAllocator);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
CleanUpForError();
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
if (!ValidateCodeAddressAndSize(codeAddress, codeSize))
{
CleanUpForError();
return KernelResult.InvalidMemRange;
}
2018-12-01 21:01:59 +01:00
result = MemoryManager.MapNewProcessCode(
codeAddress,
codePagesCount,
MemoryState.CodeStatic,
MemoryPermission.None);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
CleanUpForError();
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
result = Capabilities.InitializeForUser(caps, MemoryManager);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
CleanUpForError();
2018-12-01 21:01:59 +01:00
return result;
}
Pid = System.GetProcessId();
if (Pid == -1 || (ulong)Pid < Horizon.InitialProcessId)
{
throw new InvalidOperationException($"Invalid Process Id {Pid}.");
}
2018-12-01 21:01:59 +01:00
result = ParseProcessInfo(creationInfo);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
CleanUpForError();
}
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
private bool ValidateCodeAddressAndSize(ulong address, ulong size)
{
2018-12-01 21:01:59 +01:00
ulong codeRegionStart;
ulong codeRegionSize;
switch (MemoryManager.AddrSpaceWidth)
{
case 32:
2018-12-01 21:01:59 +01:00
codeRegionStart = 0x200000;
codeRegionSize = 0x3fe00000;
break;
case 36:
2018-12-01 21:01:59 +01:00
codeRegionStart = 0x8000000;
codeRegionSize = 0x78000000;
break;
case 39:
2018-12-01 21:01:59 +01:00
codeRegionStart = 0x8000000;
codeRegionSize = 0x7ff8000000;
break;
default: throw new InvalidOperationException("Invalid address space width on memory manager.");
}
2018-12-01 21:01:59 +01:00
ulong endAddr = address + size;
2018-12-01 21:01:59 +01:00
ulong codeRegionEnd = codeRegionStart + codeRegionSize;
2018-12-01 21:01:59 +01:00
if (endAddr <= address ||
endAddr - 1 > codeRegionEnd - 1)
{
return false;
}
2018-12-01 21:01:59 +01:00
if (MemoryManager.InsideHeapRegion (address, size) ||
MemoryManager.InsideAliasRegion(address, size))
{
return false;
}
return true;
}
2018-12-01 21:01:59 +01:00
private KernelResult ParseProcessInfo(ProcessCreationInfo creationInfo)
{
//Ensure that the current kernel version is equal or above to the minimum required.
2018-12-01 21:01:59 +01:00
uint requiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19;
uint requiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf;
if (System.EnableVersionChecks)
{
2018-12-01 21:01:59 +01:00
if (requiredKernelVersionMajor > KernelVersionMajor)
{
return KernelResult.InvalidCombination;
}
2018-12-01 21:01:59 +01:00
if (requiredKernelVersionMajor != KernelVersionMajor && requiredKernelVersionMajor < 3)
{
return KernelResult.InvalidCombination;
}
2018-12-01 21:01:59 +01:00
if (requiredKernelVersionMinor > KernelVersionMinor)
{
return KernelResult.InvalidCombination;
}
}
2018-12-01 21:01:59 +01:00
KernelResult result = AllocateThreadLocalStorage(out ulong userExceptionContextAddress);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:24:37 +01:00
UserExceptionContextAddress = userExceptionContextAddress;
2018-12-01 21:01:59 +01:00
MemoryHelper.FillWithZeros(CpuMemory, (long)userExceptionContextAddress, KTlsPageInfo.TlsEntrySize);
2018-12-01 21:01:59 +01:00
Name = creationInfo.Name;
2018-12-01 21:01:59 +01:00
_state = ProcessState.Created;
2018-12-01 21:01:59 +01:00
_creationTimestamp = PerformanceCounter.ElapsedMilliseconds;
2018-12-01 21:01:59 +01:00
MmuFlags = creationInfo.MmuFlags;
_category = creationInfo.Category;
TitleId = creationInfo.TitleId;
_entrypoint = creationInfo.CodeAddress;
_imageSize = (ulong)creationInfo.CodePagesCount * KMemoryManager.PageSize;
2018-12-01 21:01:59 +01:00
_useSystemMemBlocks = ((MmuFlags >> 6) & 1) != 0;
switch ((AddressSpaceType)((MmuFlags >> 1) & 7))
{
case AddressSpaceType.Addr32Bits:
case AddressSpaceType.Addr36Bits:
case AddressSpaceType.Addr39Bits:
2018-12-01 21:01:59 +01:00
_memoryUsageCapacity = MemoryManager.HeapRegionEnd -
MemoryManager.HeapRegionStart;
break;
case AddressSpaceType.Addr32BitsNoMap:
2018-12-01 21:01:59 +01:00
_memoryUsageCapacity = MemoryManager.HeapRegionEnd -
MemoryManager.HeapRegionStart +
MemoryManager.AliasRegionEnd -
MemoryManager.AliasRegionStart;
break;
default: throw new InvalidOperationException($"Invalid MMU flags value 0x{MmuFlags:x2}.");
}
GenerateRandomEntropy();
return KernelResult.Success;
}
2018-12-01 21:01:59 +01:00
public KernelResult AllocateThreadLocalStorage(out ulong address)
{
System.CriticalSection.Enter();
2018-12-01 21:01:59 +01:00
KernelResult result;
2018-12-01 21:01:59 +01:00
if (_freeTlsPages.Count > 0)
{
//If we have free TLS pages available, just use the first one.
2018-12-01 21:01:59 +01:00
KTlsPageInfo pageInfo = _freeTlsPages.Values.First();
2018-12-01 21:01:59 +01:00
if (!pageInfo.TryGetFreePage(out address))
{
throw new InvalidOperationException("Unexpected failure getting free TLS page!");
}
2018-12-01 21:01:59 +01:00
if (pageInfo.IsFull())
{
2018-12-01 21:01:59 +01:00
_freeTlsPages.Remove(pageInfo.PageAddr);
2018-12-01 21:01:59 +01:00
_fullTlsPages.Add(pageInfo.PageAddr, pageInfo);
}
2018-12-01 21:01:59 +01:00
result = KernelResult.Success;
}
else
{
//Otherwise, we need to create a new one.
2018-12-01 21:01:59 +01:00
result = AllocateTlsPage(out KTlsPageInfo pageInfo);
2018-12-01 21:01:59 +01:00
if (result == KernelResult.Success)
{
2018-12-01 21:01:59 +01:00
if (!pageInfo.TryGetFreePage(out address))
{
throw new InvalidOperationException("Unexpected failure getting free TLS page!");
}
2018-12-01 21:01:59 +01:00
_freeTlsPages.Add(pageInfo.PageAddr, pageInfo);
}
else
{
2018-12-01 21:01:59 +01:00
address = 0;
}
}
System.CriticalSection.Leave();
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
private KernelResult AllocateTlsPage(out KTlsPageInfo pageInfo)
{
2018-12-01 21:01:59 +01:00
pageInfo = default(KTlsPageInfo);
2018-12-01 21:01:59 +01:00
if (!System.UserSlabHeapPages.TryGetItem(out ulong tlsPagePa))
{
return KernelResult.OutOfMemory;
}
2018-12-01 21:01:59 +01:00
ulong regionStart = MemoryManager.TlsIoRegionStart;
ulong regionSize = MemoryManager.TlsIoRegionEnd - regionStart;
2018-12-01 21:01:59 +01:00
ulong regionPagesCount = regionSize / KMemoryManager.PageSize;
2018-12-01 21:01:59 +01:00
KernelResult result = MemoryManager.AllocateOrMapPa(
1,
KMemoryManager.PageSize,
2018-12-01 21:01:59 +01:00
tlsPagePa,
true,
2018-12-01 21:01:59 +01:00
regionStart,
regionPagesCount,
MemoryState.ThreadLocal,
MemoryPermission.ReadAndWrite,
2018-12-01 21:01:59 +01:00
out ulong tlsPageVa);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
2018-12-01 21:01:59 +01:00
System.UserSlabHeapPages.Free(tlsPagePa);
}
else
{
2018-12-01 21:01:59 +01:00
pageInfo = new KTlsPageInfo(tlsPageVa);
2018-12-01 21:01:59 +01:00
MemoryHelper.FillWithZeros(CpuMemory, (long)tlsPageVa, KMemoryManager.PageSize);
}
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
public KernelResult FreeThreadLocalStorage(ulong tlsSlotAddr)
{
2018-12-01 21:01:59 +01:00
ulong tlsPageAddr = BitUtils.AlignDown(tlsSlotAddr, KMemoryManager.PageSize);
System.CriticalSection.Enter();
2018-12-01 21:01:59 +01:00
KernelResult result = KernelResult.Success;
2018-12-01 21:01:59 +01:00
KTlsPageInfo pageInfo = null;
2018-12-01 21:01:59 +01:00
if (_fullTlsPages.TryGetValue(tlsPageAddr, out pageInfo))
{
//TLS page was full, free slot and move to free pages tree.
2018-12-01 21:01:59 +01:00
_fullTlsPages.Remove(tlsPageAddr);
2018-12-01 21:01:59 +01:00
_freeTlsPages.Add(tlsPageAddr, pageInfo);
}
2018-12-01 21:01:59 +01:00
else if (!_freeTlsPages.TryGetValue(tlsPageAddr, out pageInfo))
{
2018-12-01 21:01:59 +01:00
result = KernelResult.InvalidAddress;
}
2018-12-01 21:01:59 +01:00
if (pageInfo != null)
{
2018-12-01 21:01:59 +01:00
pageInfo.FreeTlsSlot(tlsSlotAddr);
2018-12-01 21:01:59 +01:00
if (pageInfo.IsEmpty())
{
//TLS page is now empty, we should ensure it is removed
//from all trees, and free the memory it was using.
2018-12-01 21:01:59 +01:00
_freeTlsPages.Remove(tlsPageAddr);
System.CriticalSection.Leave();
2018-12-01 21:01:59 +01:00
FreeTlsPage(pageInfo);
return KernelResult.Success;
}
}
System.CriticalSection.Leave();
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
private KernelResult FreeTlsPage(KTlsPageInfo pageInfo)
{
2018-12-01 21:01:59 +01:00
KernelResult result = MemoryManager.ConvertVaToPa(pageInfo.PageAddr, out ulong tlsPagePa);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
throw new InvalidOperationException("Unexpected failure translating virtual address to physical.");
}
2018-12-01 21:01:59 +01:00
result = MemoryManager.UnmapForKernel(pageInfo.PageAddr, 1, MemoryState.ThreadLocal);
2018-12-01 21:01:59 +01:00
if (result == KernelResult.Success)
{
2018-12-01 21:01:59 +01:00
System.UserSlabHeapPages.Free(tlsPagePa);
}
2018-12-01 21:01:59 +01:00
return result;
}
private void GenerateRandomEntropy()
{
//TODO.
}
2018-12-01 21:01:59 +01:00
public KernelResult Start(int mainThreadPriority, ulong stackSize)
{
2018-12-01 21:01:59 +01:00
lock (_processLock)
{
2018-12-01 21:01:59 +01:00
if (_state > ProcessState.CreatedAttached)
{
return KernelResult.InvalidState;
}
if (ResourceLimit != null && !ResourceLimit.Reserve(LimitableResource.Thread, 1))
{
return KernelResult.ResLimitExceeded;
}
2018-12-01 21:01:59 +01:00
KResourceLimit threadResourceLimit = ResourceLimit;
KResourceLimit memoryResourceLimit = null;
2018-12-01 21:01:59 +01:00
if (_mainThreadStackSize != 0)
{
throw new InvalidOperationException("Trying to start a process with a invalid state!");
}
2018-12-01 21:01:59 +01:00
ulong stackSizeRounded = BitUtils.AlignUp(stackSize, KMemoryManager.PageSize);
2018-12-01 21:01:59 +01:00
ulong neededSize = stackSizeRounded + _imageSize;
//Check if the needed size for the code and the stack will fit on the
//memory usage capacity of this Process. Also check for possible overflow
//on the above addition.
2018-12-01 21:01:59 +01:00
if (neededSize > _memoryUsageCapacity ||
neededSize < stackSizeRounded)
{
2018-12-01 21:01:59 +01:00
threadResourceLimit?.Release(LimitableResource.Thread, 1);
return KernelResult.OutOfMemory;
}
2018-12-01 21:01:59 +01:00
if (stackSizeRounded != 0 && ResourceLimit != null)
{
2018-12-01 21:01:59 +01:00
memoryResourceLimit = ResourceLimit;
2018-12-01 21:01:59 +01:00
if (!memoryResourceLimit.Reserve(LimitableResource.Memory, stackSizeRounded))
{
2018-12-01 21:01:59 +01:00
threadResourceLimit?.Release(LimitableResource.Thread, 1);
return KernelResult.ResLimitExceeded;
}
}
2018-12-01 21:01:59 +01:00
KernelResult result;
2018-12-01 21:01:59 +01:00
KThread mainThread = null;
2018-12-01 21:01:59 +01:00
ulong stackTop = 0;
void CleanUpForError()
{
2018-12-01 21:01:59 +01:00
mainThread?.Terminate();
HandleTable.Destroy();
2018-12-01 21:01:59 +01:00
if (_mainThreadStackSize != 0)
{
2018-12-01 21:01:59 +01:00
ulong stackBottom = stackTop - _mainThreadStackSize;
2018-12-01 21:01:59 +01:00
ulong stackPagesCount = _mainThreadStackSize / KMemoryManager.PageSize;
2018-12-01 21:01:59 +01:00
MemoryManager.UnmapForKernel(stackBottom, stackPagesCount, MemoryState.Stack);
}
2018-12-01 21:01:59 +01:00
memoryResourceLimit?.Release(LimitableResource.Memory, stackSizeRounded);
threadResourceLimit?.Release(LimitableResource.Thread, 1);
}
2018-12-01 21:01:59 +01:00
if (stackSizeRounded != 0)
{
2018-12-01 21:01:59 +01:00
ulong stackPagesCount = stackSizeRounded / KMemoryManager.PageSize;
2018-12-01 21:01:59 +01:00
ulong regionStart = MemoryManager.StackRegionStart;
ulong regionSize = MemoryManager.StackRegionEnd - regionStart;
2018-12-01 21:01:59 +01:00
ulong regionPagesCount = regionSize / KMemoryManager.PageSize;
2018-12-01 21:01:59 +01:00
result = MemoryManager.AllocateOrMapPa(
stackPagesCount,
KMemoryManager.PageSize,
0,
false,
2018-12-01 21:01:59 +01:00
regionStart,
regionPagesCount,
MemoryState.Stack,
MemoryPermission.ReadAndWrite,
2018-12-01 21:01:59 +01:00
out ulong stackBottom);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
CleanUpForError();
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
_mainThreadStackSize += stackSizeRounded;
2018-12-01 21:01:59 +01:00
stackTop = stackBottom + stackSizeRounded;
}
2018-12-01 21:01:59 +01:00
ulong heapCapacity = _memoryUsageCapacity - _mainThreadStackSize - _imageSize;
2018-12-01 21:01:59 +01:00
result = MemoryManager.SetHeapCapacity(heapCapacity);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
CleanUpForError();
2018-12-01 21:01:59 +01:00
return result;
}
HandleTable = new KHandleTable(System);
2018-12-01 21:01:59 +01:00
result = HandleTable.Initialize(Capabilities.HandleTableSize);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
CleanUpForError();
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
mainThread = new KThread(System);
2018-12-01 21:01:59 +01:00
result = mainThread.Initialize(
_entrypoint,
0,
2018-12-01 21:01:59 +01:00
stackTop,
mainThreadPriority,
DefaultCpuCore,
this);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
CleanUpForError();
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
result = HandleTable.GenerateHandle(mainThread, out int mainThreadHandle);
2018-12-01 21:01:59 +01:00
if (result != KernelResult.Success)
{
CleanUpForError();
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
mainThread.SetEntryArguments(0, mainThreadHandle);
2018-12-01 21:01:59 +01:00
ProcessState oldState = _state;
ProcessState newState = _state != ProcessState.Created
? ProcessState.Attached
: ProcessState.Started;
2018-12-01 21:01:59 +01:00
SetState(newState);
//TODO: We can't call KThread.Start from a non-guest thread.
//We will need to make some changes to allow the creation of
//dummy threads that will be used to initialize the current
//thread on KCoreContext so that GetCurrentThread doesn't fail.
/* Result = MainThread.Start();
if (Result != KernelResult.Success)
{
SetState(OldState);
CleanUpForError();
} */
2018-12-01 21:01:59 +01:00
mainThread.Reschedule(ThreadSchedState.Running);
2018-12-01 21:01:59 +01:00
return result;
}
}
2018-12-01 21:01:59 +01:00
private void SetState(ProcessState newState)
{
2018-12-01 21:01:59 +01:00
if (_state != newState)
{
2018-12-01 21:01:59 +01:00
_state = newState;
_signaled = true;
Signal();
}
}
public KernelResult InitializeThread(
2018-12-01 21:01:59 +01:00
KThread thread,
ulong entrypoint,
ulong argsPtr,
ulong stackTop,
int priority,
int cpuCore)
{
2018-12-01 21:01:59 +01:00
lock (_processLock)
{
2018-12-01 21:01:59 +01:00
return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this);
}
}
2018-12-01 21:01:59 +01:00
public void SubscribeThreadEventHandlers(CpuThread context)
{
2018-12-01 21:01:59 +01:00
context.ThreadState.Interrupt += InterruptHandler;
context.ThreadState.SvcCall += _svcHandler.SvcCall;
}
private void InterruptHandler(object sender, EventArgs e)
{
System.Scheduler.ContextSwitch();
}
public void IncrementThreadCount()
{
2018-12-01 21:01:59 +01:00
Interlocked.Increment(ref _threadCount);
System.ThreadCounter.AddCount();
}
public void DecrementThreadCountAndTerminateIfZero()
{
System.ThreadCounter.Signal();
2018-12-01 21:01:59 +01:00
if (Interlocked.Decrement(ref _threadCount) == 0)
{
Terminate();
}
}
public ulong GetMemoryCapacity()
{
2018-12-01 21:01:59 +01:00
ulong totalCapacity = (ulong)ResourceLimit.GetRemainingValue(LimitableResource.Memory);
2018-12-01 21:01:59 +01:00
totalCapacity += MemoryManager.GetTotalHeapSize();
2018-12-01 21:01:59 +01:00
totalCapacity += GetPersonalMmHeapSize();
2018-12-01 21:01:59 +01:00
totalCapacity += _imageSize + _mainThreadStackSize;
2018-12-01 21:01:59 +01:00
if (totalCapacity <= _memoryUsageCapacity)
{
2018-12-01 21:01:59 +01:00
return totalCapacity;
}
2018-12-01 21:01:59 +01:00
return _memoryUsageCapacity;
}
public ulong GetMemoryUsage()
{
2018-12-01 21:01:59 +01:00
return _imageSize + _mainThreadStackSize + MemoryManager.GetTotalHeapSize() + GetPersonalMmHeapSize();
}
public ulong GetMemoryCapacityWithoutPersonalMmHeap()
{
return GetMemoryCapacity() - GetPersonalMmHeapSize();
}
public ulong GetMemoryUsageWithoutPersonalMmHeap()
{
return GetMemoryUsage() - GetPersonalMmHeapSize();
}
private ulong GetPersonalMmHeapSize()
{
2018-12-01 21:01:59 +01:00
return GetPersonalMmHeapSize(PersonalMmHeapPagesCount, _memRegion);
}
2018-12-01 21:01:59 +01:00
private static ulong GetPersonalMmHeapSize(ulong personalMmHeapPagesCount, MemoryRegion memRegion)
{
2018-12-01 21:01:59 +01:00
if (memRegion == MemoryRegion.Applet)
{
return 0;
}
2018-12-01 21:01:59 +01:00
return personalMmHeapPagesCount * KMemoryManager.PageSize;
}
2018-12-01 21:01:59 +01:00
public void AddThread(KThread thread)
{
2018-12-01 21:01:59 +01:00
lock (_threadingLock)
{
2018-12-01 21:01:59 +01:00
thread.ProcessListNode = _threads.AddLast(thread);
}
}
2018-12-01 21:01:59 +01:00
public void RemoveThread(KThread thread)
{
2018-12-01 21:01:59 +01:00
lock (_threadingLock)
{
2018-12-01 21:01:59 +01:00
_threads.Remove(thread.ProcessListNode);
}
}
2018-12-01 21:01:59 +01:00
public bool IsCpuCoreAllowed(int core)
{
2018-12-01 21:01:59 +01:00
return (Capabilities.AllowedCpuCoresMask & (1L << core)) != 0;
}
2018-12-01 21:01:59 +01:00
public bool IsPriorityAllowed(int priority)
{
2018-12-01 21:01:59 +01:00
return (Capabilities.AllowedThreadPriosMask & (1L << priority)) != 0;
}
public override bool IsSignaled()
{
2018-12-01 21:01:59 +01:00
return _signaled;
}
public KernelResult Terminate()
{
2018-12-01 21:01:59 +01:00
KernelResult result;
2018-12-01 21:01:59 +01:00
bool shallTerminate = false;
System.CriticalSection.Enter();
2018-12-01 21:01:59 +01:00
lock (_processLock)
{
2018-12-01 21:01:59 +01:00
if (_state >= ProcessState.Started)
{
2018-12-01 21:01:59 +01:00
if (_state == ProcessState.Started ||
_state == ProcessState.Crashed ||
_state == ProcessState.Attached ||
_state == ProcessState.DebugSuspended)
{
SetState(ProcessState.Exiting);
2018-12-01 21:01:59 +01:00
shallTerminate = true;
}
2018-12-01 21:01:59 +01:00
result = KernelResult.Success;
}
else
{
2018-12-01 21:01:59 +01:00
result = KernelResult.InvalidState;
}
}
System.CriticalSection.Leave();
2018-12-01 21:01:59 +01:00
if (shallTerminate)
{
//UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
HandleTable.Destroy();
SignalExitForDebugEvent();
SignalExit();
}
2018-12-01 21:01:59 +01:00
return result;
}
2018-12-01 21:01:59 +01:00
private void UnpauseAndTerminateAllThreadsExcept(KThread thread)
{
//TODO.
}
private void SignalExitForDebugEvent()
{
//TODO: Debug events.
}
private void SignalExit()
{
if (ResourceLimit != null)
{
ResourceLimit.Release(LimitableResource.Memory, GetMemoryUsage());
}
System.CriticalSection.Enter();
SetState(ProcessState.Exited);
System.CriticalSection.Leave();
}
public KernelResult ClearIfNotExited()
{
2018-12-01 21:01:59 +01:00
KernelResult result;
System.CriticalSection.Enter();
2018-12-01 21:01:59 +01:00
lock (_processLock)
{
2018-12-01 21:01:59 +01:00
if (_state != ProcessState.Exited && _signaled)
{
2018-12-01 21:01:59 +01:00
_signaled = false;
2018-12-01 21:01:59 +01:00
result = KernelResult.Success;
}
else
{
2018-12-01 21:01:59 +01:00
result = KernelResult.InvalidState;
}
}
System.CriticalSection.Leave();
2018-12-01 21:01:59 +01:00
return result;
}
public void StopAllThreads()
{
2018-12-01 21:01:59 +01:00
lock (_threadingLock)
{
2018-12-01 21:01:59 +01:00
foreach (KThread thread in _threads)
{
2018-12-01 21:01:59 +01:00
thread.Context.StopExecution();
2018-12-01 21:01:59 +01:00
System.Scheduler.CoreManager.Set(thread.Context.Work);
}
}
}
private void InvalidAccessHandler(object sender, InvalidAccessEventArgs e)
{
PrintCurrentThreadStackTrace();
}
public void PrintCurrentThreadStackTrace()
{
System.Scheduler.GetCurrentThread().PrintGuestStackTrace();
}
private void CpuTraceHandler(object sender, CpuTraceEventArgs e)
{
Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}.");
}
}
}