aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs15
-rw-r--r--Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs51
-rw-r--r--Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs310
-rw-r--r--Ryujinx.HLE/HOS/Kernel/HleScheduler.cs16
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs244
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KAutoObject.cs42
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KClientPort.cs31
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs71
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs83
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KCoreContext.cs31
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs (renamed from Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs)8
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KHandleTable.cs (renamed from Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs)48
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs22
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs16
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs22
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs19
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs10
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs2155
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs43
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs428
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KPageList.cs80
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KPageNode.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KPort.cs26
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KProcess.cs1013
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs311
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs8
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs146
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KScheduler.cs11
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KServerPort.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs64
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs50
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KSynchronization.cs18
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs8
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KThread.cs337
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KTimeManager.cs53
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs73
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs10
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KernelInit.cs136
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KernelResult.cs29
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs71
-rw-r--r--Ryujinx.HLE/HOS/Kernel/LimitableResource.cs13
-rw-r--r--Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs12
-rw-r--r--Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs10
-rw-r--r--Ryujinx.HLE/HOS/Kernel/MemoryState.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs128
-rw-r--r--Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs37
-rw-r--r--Ryujinx.HLE/HOS/Kernel/ProcessState.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SvcHandler.cs34
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SvcMemory.cs278
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SvcSystem.cs584
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SvcThread.cs198
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs40
-rw-r--r--Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/ThreadType.cs10
56 files changed, 6399 insertions, 1116 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs b/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs
index c97caf42..6f7b230e 100644
--- a/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs
+++ b/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs
@@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
Addr32Bits = 0,
Addr36Bits = 1,
- Addr36BitsNoMap = 2,
+ Addr32BitsNoMap = 2,
Addr39Bits = 3
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs b/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs
index 8a2d47f7..b584d719 100644
--- a/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs
+++ b/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs
@@ -6,4 +6,4 @@ namespace Ryujinx.HLE.HOS.Kernel
DecrementAndWaitIfLessThan = 1,
WaitIfEqual = 2
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs b/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs
new file mode 100644
index 00000000..b20a83e2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ static class DramMemoryMap
+ {
+ public const ulong DramBase = 0x80000000;
+ public const ulong DramSize = 0x100000000;
+ public const ulong DramEnd = DramBase + DramSize;
+
+ public const ulong KernelReserveBase = DramBase + 0x60000;
+
+ public const ulong SlabHeapBase = KernelReserveBase + 0x85000;
+ public const ulong SlapHeapSize = 0xa21000;
+ public const ulong SlabHeapEnd = SlabHeapBase + SlapHeapSize;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs b/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs
index 0bfa2710..6a424cf2 100644
--- a/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs
@@ -5,24 +5,61 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class HleCoreManager
{
- private ConcurrentDictionary<Thread, ManualResetEvent> Threads;
+ private class PausableThread
+ {
+ public ManualResetEvent Event { get; private set; }
+
+ public bool IsExiting { get; set; }
+
+ public PausableThread()
+ {
+ Event = new ManualResetEvent(false);
+ }
+ }
+
+ private ConcurrentDictionary<Thread, PausableThread> Threads;
public HleCoreManager()
{
- Threads = new ConcurrentDictionary<Thread, ManualResetEvent>();
+ Threads = new ConcurrentDictionary<Thread, PausableThread>();
+ }
+
+ public void Set(Thread Thread)
+ {
+ GetThread(Thread).Event.Set();
+ }
+
+ public void Reset(Thread Thread)
+ {
+ GetThread(Thread).Event.Reset();
+ }
+
+ public void Wait(Thread Thread)
+ {
+ PausableThread PausableThread = GetThread(Thread);
+
+ if (!PausableThread.IsExiting)
+ {
+ PausableThread.Event.WaitOne();
+ }
+ }
+
+ public void Exit(Thread Thread)
+ {
+ GetThread(Thread).IsExiting = true;
}
- public ManualResetEvent GetThread(Thread Thread)
+ private PausableThread GetThread(Thread Thread)
{
- return Threads.GetOrAdd(Thread, (Key) => new ManualResetEvent(false));
+ return Threads.GetOrAdd(Thread, (Key) => new PausableThread());
}
public void RemoveThread(Thread Thread)
{
- if (Threads.TryRemove(Thread, out ManualResetEvent Event))
+ if (Threads.TryRemove(Thread, out PausableThread PausableThread))
{
- Event.Set();
- Event.Dispose();
+ PausableThread.Event.Set();
+ PausableThread.Event.Dispose();
}
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs b/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs
new file mode 100644
index 00000000..a6053b1b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs
@@ -0,0 +1,310 @@
+using ChocolArm64.Memory;
+using ChocolArm64.State;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Diagnostics.Demangler;
+using Ryujinx.HLE.Loaders.Elf;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class HleProcessDebugger
+ {
+ private const int Mod0 = 'M' << 0 | 'O' << 8 | 'D' << 16 | '0' << 24;
+
+ private KProcess Owner;
+
+ private class Image
+ {
+ public long BaseAddress { get; private set; }
+
+ public ElfSymbol[] Symbols { get; private set; }
+
+ public Image(long BaseAddress, ElfSymbol[] Symbols)
+ {
+ this.BaseAddress = BaseAddress;
+ this.Symbols = Symbols;
+ }
+ }
+
+ private List<Image> Images;
+
+ private int Loaded;
+
+ public HleProcessDebugger(KProcess Owner)
+ {
+ this.Owner = Owner;
+
+ Images = new List<Image>();
+ }
+
+ public void PrintGuestStackTrace(CpuThreadState ThreadState)
+ {
+ EnsureLoaded();
+
+ StringBuilder Trace = new StringBuilder();
+
+ Trace.AppendLine("Guest stack trace:");
+
+ void AppendTrace(long Address)
+ {
+ Image Image = GetImage(Address, out int ImageIndex);
+
+ if (Image == null || !TryGetSubName(Image, Address, out string SubName))
+ {
+ SubName = $"Sub{Address:x16}";
+ }
+ else if (SubName.StartsWith("_Z"))
+ {
+ SubName = Demangler.Parse(SubName);
+ }
+
+ if (Image != null)
+ {
+ long Offset = Address - Image.BaseAddress;
+
+ string ImageName = GetGuessedNsoNameFromIndex(ImageIndex);
+
+ string ImageNameAndOffset = $"[{Owner.Name}] {ImageName}:0x{Offset:x8}";
+
+ Trace.AppendLine($" {ImageNameAndOffset} {SubName}");
+ }
+ else
+ {
+ Trace.AppendLine($" [{Owner.Name}] ??? {SubName}");
+ }
+ }
+
+ long FramePointer = (long)ThreadState.X29;
+
+ while (FramePointer != 0)
+ {
+ if ((FramePointer & 7) != 0 ||
+ !Owner.CpuMemory.IsMapped(FramePointer) ||
+ !Owner.CpuMemory.IsMapped(FramePointer + 8))
+ {
+ break;
+ }
+
+ //Note: This is the return address, we need to subtract one instruction
+ //worth of bytes to get the branch instruction address.
+ AppendTrace(Owner.CpuMemory.ReadInt64(FramePointer + 8) - 4);
+
+ FramePointer = Owner.CpuMemory.ReadInt64(FramePointer);
+ }
+
+ Logger.PrintInfo(LogClass.Cpu, Trace.ToString());
+ }
+
+ private bool TryGetSubName(Image Image, long Address, out string Name)
+ {
+ Address -= Image.BaseAddress;
+
+ int Left = 0;
+ int Right = Image.Symbols.Length - 1;
+
+ while (Left <= Right)
+ {
+ int Size = Right - Left;
+
+ int Middle = Left + (Size >> 1);
+
+ ElfSymbol Symbol = Image.Symbols[Middle];
+
+ long EndAddr = Symbol.Value + Symbol.Size;
+
+ if ((ulong)Address >= (ulong)Symbol.Value && (ulong)Address < (ulong)EndAddr)
+ {
+ Name = Symbol.Name;
+
+ return true;
+ }
+
+ if ((ulong)Address < (ulong)Symbol.Value)
+ {
+ Right = Middle - 1;
+ }
+ else
+ {
+ Left = Middle + 1;
+ }
+ }
+
+ Name = null;
+
+ return false;
+ }
+
+ private Image GetImage(long Address, out int Index)
+ {
+ lock (Images)
+ {
+ for (Index = Images.Count - 1; Index >= 0; Index--)
+ {
+ if ((ulong)Address >= (ulong)Images[Index].BaseAddress)
+ {
+ return Images[Index];
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private string GetGuessedNsoNameFromIndex(int Index)
+ {
+ if ((uint)Index > 11)
+ {
+ return "???";
+ }
+
+ if (Index == 0)
+ {
+ return "rtld";
+ }
+ else if (Index == 1)
+ {
+ return "main";
+ }
+ else if (Index == GetImagesCount() - 1)
+ {
+ return "sdk";
+ }
+ else
+ {
+ return "subsdk" + (Index - 2);
+ }
+ }
+
+ private int GetImagesCount()
+ {
+ lock (Images)
+ {
+ return Images.Count;
+ }
+ }
+
+ private void EnsureLoaded()
+ {
+ if (Interlocked.CompareExchange(ref Loaded, 1, 0) == 0)
+ {
+ ScanMemoryForTextSegments();
+ }
+ }
+
+ private void ScanMemoryForTextSegments()
+ {
+ ulong OldAddress = 0;
+ ulong Address = 0;
+
+ while (Address >= OldAddress)
+ {
+ KMemoryInfo Info = Owner.MemoryManager.QueryMemory(Address);
+
+ if (Info.State == MemoryState.Reserved)
+ {
+ break;
+ }
+
+ if (Info.State == MemoryState.CodeStatic && Info.Permission == MemoryPermission.ReadAndExecute)
+ {
+ LoadMod0Symbols(Owner.CpuMemory, (long)Info.Address);
+ }
+
+ OldAddress = Address;
+
+ Address = Info.Address + Info.Size;
+ }
+ }
+
+ private void LoadMod0Symbols(MemoryManager Memory, long TextOffset)
+ {
+ long Mod0Offset = TextOffset + Memory.ReadUInt32(TextOffset + 4);
+
+ if (Mod0Offset < TextOffset || !Memory.IsMapped(Mod0Offset) || (Mod0Offset & 3) != 0)
+ {
+ return;
+ }
+
+ Dictionary<ElfDynamicTag, long> Dynamic = new Dictionary<ElfDynamicTag, long>();
+
+ int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
+
+ if (Mod0Magic != Mod0)
+ {
+ return;
+ }
+
+ long DynamicOffset = Memory.ReadInt32(Mod0Offset + 0x4) + Mod0Offset;
+ long BssStartOffset = Memory.ReadInt32(Mod0Offset + 0x8) + Mod0Offset;
+ long BssEndOffset = Memory.ReadInt32(Mod0Offset + 0xc) + Mod0Offset;
+ long EhHdrStartOffset = Memory.ReadInt32(Mod0Offset + 0x10) + Mod0Offset;
+ long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
+ long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
+
+ while (true)
+ {
+ long TagVal = Memory.ReadInt64(DynamicOffset + 0);
+ long Value = Memory.ReadInt64(DynamicOffset + 8);
+
+ DynamicOffset += 0x10;
+
+ ElfDynamicTag Tag = (ElfDynamicTag)TagVal;
+
+ if (Tag == ElfDynamicTag.DT_NULL)
+ {
+ break;
+ }
+
+ Dynamic[Tag] = Value;
+ }
+
+ if (!Dynamic.TryGetValue(ElfDynamicTag.DT_STRTAB, out long StrTab) ||
+ !Dynamic.TryGetValue(ElfDynamicTag.DT_SYMTAB, out long SymTab) ||
+ !Dynamic.TryGetValue(ElfDynamicTag.DT_SYMENT, out long SymEntSize))
+ {
+ return;
+ }
+
+ long StrTblAddr = TextOffset + StrTab;
+ long SymTblAddr = TextOffset + SymTab;
+
+ List<ElfSymbol> Symbols = new List<ElfSymbol>();
+
+ while ((ulong)SymTblAddr < (ulong)StrTblAddr)
+ {
+ ElfSymbol Sym = GetSymbol(Memory, SymTblAddr, StrTblAddr);
+
+ Symbols.Add(Sym);
+
+ SymTblAddr += SymEntSize;
+ }
+
+ lock (Images)
+ {
+ Images.Add(new Image(TextOffset, Symbols.OrderBy(x => x.Value).ToArray()));
+ }
+ }
+
+ private ElfSymbol GetSymbol(MemoryManager Memory, long Address, long StrTblAddr)
+ {
+ int NameIndex = Memory.ReadInt32(Address + 0);
+ int Info = Memory.ReadByte (Address + 4);
+ int Other = Memory.ReadByte (Address + 5);
+ int SHIdx = Memory.ReadInt16(Address + 6);
+ long Value = Memory.ReadInt64(Address + 8);
+ long Size = Memory.ReadInt64(Address + 16);
+
+ string Name = string.Empty;
+
+ for (int Chr; (Chr = Memory.ReadByte(StrTblAddr + NameIndex++)) != 0;)
+ {
+ Name += (char)Chr;
+ }
+
+ return new ElfSymbol(Name, Info, Other, SHIdx, Value, Size);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs
index e0cb158c..87dbe553 100644
--- a/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs
@@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel
public bool MultiCoreScheduling { get; set; }
- private HleCoreManager CoreManager;
+ public HleCoreManager CoreManager { get; private set; }
private bool KeepPreempting;
@@ -49,11 +49,11 @@ namespace Ryujinx.HLE.HOS.Kernel
if (SelectedCount == 0)
{
- CoreManager.GetThread(Thread.CurrentThread).Reset();
+ CoreManager.Reset(Thread.CurrentThread);
}
else if (SelectedCount == 1)
{
- CoreManager.GetThread(Thread.CurrentThread).Set();
+ CoreManager.Set(Thread.CurrentThread);
}
else
{
@@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- CoreManager.GetThread(CurrentThread.Context.Work).Reset();
+ CoreManager.Reset(CurrentThread.Context.Work);
}
//Advance current core and try picking a thread,
@@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
CoreContext.CurrentThread.ClearExclusive();
- CoreManager.GetThread(CoreContext.CurrentThread.Context.Work).Set();
+ CoreManager.Set(CoreContext.CurrentThread.Context.Work);
CoreContext.CurrentThread.Context.Execute();
@@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- CoreManager.GetThread(Thread.CurrentThread).WaitOne();
+ CoreManager.Wait(Thread.CurrentThread);
}
private void PreemptCurrentThread()
@@ -134,11 +134,11 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- public void StopThread(KThread Thread)
+ public void ExitThread(KThread Thread)
{
Thread.Context.StopExecution();
- CoreManager.GetThread(Thread.Context.Work).Set();
+ CoreManager.Exit(Thread.Context.Work);
}
public void RemoveThread(KThread Thread)
diff --git a/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs
index 4a0f955f..cc637be0 100644
--- a/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs
@@ -1,4 +1,3 @@
-using ChocolArm64.Memory;
using System.Collections.Generic;
using System.Linq;
@@ -23,39 +22,36 @@ namespace Ryujinx.HLE.HOS.Kernel
ArbiterThreads = new List<KThread>();
}
- public long ArbitrateLock(
- Process Process,
- MemoryManager Memory,
- int OwnerHandle,
- long MutexAddress,
- int RequesterHandle)
+ public long ArbitrateLock(int OwnerHandle, long MutexAddress, int RequesterHandle)
{
- System.CriticalSectionLock.Lock();
-
KThread CurrentThread = System.Scheduler.GetCurrentThread();
+ System.CriticalSection.Enter();
+
CurrentThread.SignaledObj = null;
CurrentThread.ObjSyncResult = 0;
- if (!UserToKernelInt32(Memory, MutexAddress, out int MutexValue))
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (!KernelTransfer.UserToKernelInt32(System, MutexAddress, out int MutexValue))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);;
}
if (MutexValue != (OwnerHandle | HasListenersMask))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return 0;
}
- KThread MutexOwner = Process.HandleTable.GetObject<KThread>(OwnerHandle);
+ KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(OwnerHandle);
if (MutexOwner == null)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
@@ -67,26 +63,26 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.Reschedule(ThreadSchedState.Paused);
- System.CriticalSectionLock.Unlock();
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Leave();
+ System.CriticalSection.Enter();
if (CurrentThread.MutexOwner != null)
{
CurrentThread.MutexOwner.RemoveMutexWaiter(CurrentThread);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return (uint)CurrentThread.ObjSyncResult;
}
- public long ArbitrateUnlock(MemoryManager Memory, long MutexAddress)
+ public long ArbitrateUnlock(long MutexAddress)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
KThread CurrentThread = System.Scheduler.GetCurrentThread();
- (long Result, KThread NewOwnerThread) = MutexUnlock(Memory, CurrentThread, MutexAddress);
+ (long Result, KThread NewOwnerThread) = MutexUnlock(CurrentThread, MutexAddress);
if (Result != 0 && NewOwnerThread != null)
{
@@ -94,19 +90,18 @@ namespace Ryujinx.HLE.HOS.Kernel
NewOwnerThread.ObjSyncResult = (int)Result;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
public long WaitProcessWideKeyAtomic(
- MemoryManager Memory,
- long MutexAddress,
- long CondVarAddress,
- int ThreadHandle,
- long Timeout)
+ long MutexAddress,
+ long CondVarAddress,
+ int ThreadHandle,
+ long Timeout)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
KThread CurrentThread = System.Scheduler.GetCurrentThread();
@@ -116,16 +111,16 @@ namespace Ryujinx.HLE.HOS.Kernel
if (CurrentThread.ShallBeTerminated ||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
}
- (long Result, _) = MutexUnlock(Memory, CurrentThread, MutexAddress);
+ (long Result, _) = MutexUnlock(CurrentThread, MutexAddress);
if (Result != 0)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
@@ -146,14 +141,14 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
if (Timeout > 0)
{
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
}
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (CurrentThread.MutexOwner != null)
{
@@ -162,12 +157,12 @@ namespace Ryujinx.HLE.HOS.Kernel
CondVarThreads.Remove(CurrentThread);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return (uint)CurrentThread.ObjSyncResult;
}
- private (long, KThread) MutexUnlock(MemoryManager Memory, KThread CurrentThread, long MutexAddress)
+ private (long, KThread) MutexUnlock(KThread CurrentThread, long MutexAddress)
{
KThread NewOwnerThread = CurrentThread.RelinquishMutex(MutexAddress, out int Count);
@@ -190,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Kernel
long Result = 0;
- if (!KernelToUserInt32(Memory, MutexAddress, MutexValue))
+ if (!KernelTransfer.KernelToUserInt32(System, MutexAddress, MutexValue))
{
Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
@@ -198,17 +193,17 @@ namespace Ryujinx.HLE.HOS.Kernel
return (Result, NewOwnerThread);
}
- public void SignalProcessWideKey(Process Process, MemoryManager Memory, long Address, int Count)
+ public void SignalProcessWideKey(long Address, int Count)
{
Queue<KThread> SignaledThreads = new Queue<KThread>();
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
IOrderedEnumerable<KThread> SortedThreads = CondVarThreads.OrderBy(x => x.DynamicPriority);
foreach (KThread Thread in SortedThreads.Where(x => x.CondVarAddress == Address))
{
- TryAcquireMutex(Process, Memory, Thread);
+ TryAcquireMutex(Thread);
SignaledThreads.Enqueue(Thread);
@@ -224,19 +219,21 @@ namespace Ryujinx.HLE.HOS.Kernel
CondVarThreads.Remove(Thread);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
- private KThread TryAcquireMutex(Process Process, MemoryManager Memory, KThread Requester)
+ private KThread TryAcquireMutex(KThread Requester)
{
long Address = Requester.MutexAddress;
- Memory.SetExclusive(0, Address);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- if (!UserToKernelInt32(Memory, Address, out int MutexValue))
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
+
+ if (!KernelTransfer.UserToKernelInt32(System, Address, out int MutexValue))
{
//Invalid address.
- Memory.ClearExclusive(0);
+ CurrentProcess.CpuMemory.ClearExclusive(0);
Requester.SignaledObj = null;
Requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@@ -246,27 +243,27 @@ namespace Ryujinx.HLE.HOS.Kernel
while (true)
{
- if (Memory.TestExclusive(0, Address))
+ if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
{
if (MutexValue != 0)
{
//Update value to indicate there is a mutex waiter now.
- Memory.WriteInt32(Address, MutexValue | HasListenersMask);
+ CurrentProcess.CpuMemory.WriteInt32(Address, MutexValue | HasListenersMask);
}
else
{
//No thread owning the mutex, assign to requesting thread.
- Memory.WriteInt32(Address, Requester.ThreadHandleForUserMutex);
+ CurrentProcess.CpuMemory.WriteInt32(Address, Requester.ThreadHandleForUserMutex);
}
- Memory.ClearExclusiveForStore(0);
+ CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
break;
}
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- MutexValue = Memory.ReadInt32(Address);
+ MutexValue = CurrentProcess.CpuMemory.ReadInt32(Address);
}
if (MutexValue == 0)
@@ -282,7 +279,7 @@ namespace Ryujinx.HLE.HOS.Kernel
MutexValue &= ~HasListenersMask;
- KThread MutexOwner = Process.HandleTable.GetObject<KThread>(MutexValue);
+ KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(MutexValue);
if (MutexOwner != null)
{
@@ -301,16 +298,16 @@ namespace Ryujinx.HLE.HOS.Kernel
return MutexOwner;
}
- public long WaitForAddressIfEqual(MemoryManager Memory, long Address, int Value, long Timeout)
+ public long WaitForAddressIfEqual(long Address, int Value, long Timeout)
{
KThread CurrentThread = System.Scheduler.GetCurrentThread();
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (CurrentThread.ShallBeTerminated ||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
}
@@ -318,9 +315,9 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.SignaledObj = null;
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
- if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+ if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
@@ -329,7 +326,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
if (Timeout == 0)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
}
@@ -346,14 +343,14 @@ namespace Ryujinx.HLE.HOS.Kernel
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
if (Timeout > 0)
{
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
}
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (CurrentThread.WaitingInArbitration)
{
@@ -362,31 +359,26 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.WaitingInArbitration = false;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return CurrentThread.ObjSyncResult;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
- public long WaitForAddressIfLessThan(
- MemoryManager Memory,
- long Address,
- int Value,
- bool ShouldDecrement,
- long Timeout)
+ public long WaitForAddressIfLessThan(long Address, int Value, bool ShouldDecrement, long Timeout)
{
KThread CurrentThread = System.Scheduler.GetCurrentThread();
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (CurrentThread.ShallBeTerminated ||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
}
@@ -394,12 +386,14 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.SignaledObj = null;
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
//If ShouldDecrement is true, do atomic decrement of the value at Address.
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+ if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
@@ -408,28 +402,28 @@ namespace Ryujinx.HLE.HOS.Kernel
{
while (CurrentValue < Value)
{
- if (Memory.TestExclusive(0, Address))
+ if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
{
- Memory.WriteInt32(Address, CurrentValue - 1);
+ CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue - 1);
- Memory.ClearExclusiveForStore(0);
+ CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
break;
}
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- CurrentValue = Memory.ReadInt32(Address);
+ CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
}
}
- Memory.ClearExclusive(0);
+ CurrentProcess.CpuMemory.ClearExclusive(0);
if (CurrentValue < Value)
{
if (Timeout == 0)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
}
@@ -446,14 +440,14 @@ namespace Ryujinx.HLE.HOS.Kernel
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
if (Timeout > 0)
{
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
}
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (CurrentThread.WaitingInArbitration)
{
@@ -462,12 +456,12 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.WaitingInArbitration = false;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return CurrentThread.ObjSyncResult;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
@@ -498,63 +492,65 @@ namespace Ryujinx.HLE.HOS.Kernel
public long Signal(long Address, int Count)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
WakeArbiterThreads(Address, Count);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return 0;
}
- public long SignalAndIncrementIfEqual(MemoryManager Memory, long Address, int Value, int Count)
+ public long SignalAndIncrementIfEqual(long Address, int Value, int Count)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+ if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
while (CurrentValue == Value)
{
- if (Memory.TestExclusive(0, Address))
+ if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
{
- Memory.WriteInt32(Address, CurrentValue + 1);
+ CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + 1);
- Memory.ClearExclusiveForStore(0);
+ CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
break;
}
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- CurrentValue = Memory.ReadInt32(Address);
+ CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
}
- Memory.ClearExclusive(0);
+ CurrentProcess.CpuMemory.ClearExclusive(0);
if (CurrentValue != Value)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
WakeArbiterThreads(Address, Count);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return 0;
}
- public long SignalAndModifyIfEqual(MemoryManager Memory, long Address, int Value, int Count)
+ public long SignalAndModifyIfEqual(long Address, int Value, int Count)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
int Offset;
@@ -580,43 +576,45 @@ namespace Ryujinx.HLE.HOS.Kernel
Offset = 1;
}
- Memory.SetExclusive(0, Address);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
+
+ if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
while (CurrentValue == Value)
{
- if (Memory.TestExclusive(0, Address))
+ if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
{
- Memory.WriteInt32(Address, CurrentValue + Offset);
+ CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + Offset);
- Memory.ClearExclusiveForStore(0);
+ CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
break;
}
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- CurrentValue = Memory.ReadInt32(Address);
+ CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
}
- Memory.ClearExclusive(0);
+ CurrentProcess.CpuMemory.ClearExclusive(0);
if (CurrentValue != Value)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
WakeArbiterThreads(Address, Count);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return 0;
}
@@ -648,31 +646,5 @@ namespace Ryujinx.HLE.HOS.Kernel
ArbiterThreads.Remove(Thread);
}
}
-
- private bool UserToKernelInt32(MemoryManager Memory, long Address, out int Value)
- {
- if (Memory.IsMapped(Address))
- {
- Value = Memory.ReadInt32(Address);
-
- return true;
- }
-
- Value = 0;
-
- return false;
- }
-
- private bool KernelToUserInt32(MemoryManager Memory, long Address, int Value)
- {
- if (Memory.IsMapped(Address))
- {
- Memory.WriteInt32ToSharedAddr(Address, Value);
-
- return true;
- }
-
- return false;
- }
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/KAutoObject.cs b/Ryujinx.HLE/HOS/Kernel/KAutoObject.cs
new file mode 100644
index 00000000..a91bf9a8
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KAutoObject.cs
@@ -0,0 +1,42 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KAutoObject
+ {
+ protected Horizon System;
+
+ public KAutoObject(Horizon System)
+ {
+ this.System = System;
+ }
+
+ public virtual KernelResult SetName(string Name)
+ {
+ if (!System.AutoObjectNames.TryAdd(Name, this))
+ {
+ return KernelResult.InvalidState;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public static KernelResult RemoveName(Horizon System, string Name)
+ {
+ if (!System.AutoObjectNames.TryRemove(Name, out _))
+ {
+ return KernelResult.NotFound;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public static KAutoObject FindNamedObject(Horizon System, string Name)
+ {
+ if (System.AutoObjectNames.TryGetValue(Name, out KAutoObject Obj))
+ {
+ return Obj;
+ }
+
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KClientPort.cs b/Ryujinx.HLE/HOS/Kernel/KClientPort.cs
new file mode 100644
index 00000000..e3f8128b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KClientPort.cs
@@ -0,0 +1,31 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KClientPort : KSynchronizationObject
+ {
+ private int SessionsCount;
+ private int CurrentCapacity;
+ private int MaxSessions;
+
+ private KPort Parent;
+
+ public KClientPort(Horizon System) : base(System) { }
+
+ public void Initialize(KPort Parent, int MaxSessions)
+ {
+ this.MaxSessions = MaxSessions;
+ this.Parent = Parent;
+ }
+
+ public new static KernelResult RemoveName(Horizon System, string Name)
+ {
+ KAutoObject FoundObj = KAutoObject.FindNamedObject(System, Name);
+
+ if (!(FoundObj is KClientPort))
+ {
+ return KernelResult.NotFound;
+ }
+
+ return KAutoObject.RemoveName(System, Name);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs b/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs
new file mode 100644
index 00000000..1c95f811
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs
@@ -0,0 +1,71 @@
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ static class KConditionVariable
+ {
+ public static void Wait(Horizon System, LinkedList<KThread> ThreadList, object Mutex, long Timeout)
+ {
+ KThread CurrentThread = System.Scheduler.GetCurrentThread();
+
+ System.CriticalSection.Enter();
+
+ Monitor.Exit(Mutex);
+
+ CurrentThread.Withholder = ThreadList;
+
+ CurrentThread.Reschedule(ThreadSchedState.Paused);
+
+ CurrentThread.WithholderNode = ThreadList.AddLast(CurrentThread);
+
+ if (CurrentThread.ShallBeTerminated ||
+ CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
+ {
+ ThreadList.Remove(CurrentThread.WithholderNode);
+
+ CurrentThread.Reschedule(ThreadSchedState.Running);
+
+ CurrentThread.Withholder = null;
+
+ System.CriticalSection.Leave();
+ }
+ else
+ {
+ if (Timeout > 0)
+ {
+ System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
+ }
+
+ System.CriticalSection.Leave();
+
+ if (Timeout > 0)
+ {
+ System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
+ }
+ }
+
+ Monitor.Enter(Mutex);
+ }
+
+ public static void NotifyAll(Horizon System, LinkedList<KThread> ThreadList)
+ {
+ System.CriticalSection.Enter();
+
+ LinkedListNode<KThread> Node = ThreadList.First;
+
+ for (; Node != null; Node = ThreadList.First)
+ {
+ KThread Thread = Node.Value;
+
+ ThreadList.Remove(Thread.WithholderNode);
+
+ Thread.Withholder = null;
+
+ Thread.Reschedule(ThreadSchedState.Running);
+ }
+
+ System.CriticalSection.Leave();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs b/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs
new file mode 100644
index 00000000..03e7dddf
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs
@@ -0,0 +1,83 @@
+using Ryujinx.Common;
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KContextIdManager
+ {
+ private const int IdMasksCount = 8;
+
+ private int[] IdMasks;
+
+ private int NextFreeBitHint;
+
+ public KContextIdManager()
+ {
+ IdMasks = new int[IdMasksCount];
+ }
+
+ public int GetId()
+ {
+ lock (IdMasks)
+ {
+ int Id = 0;
+
+ if (!TestBit(NextFreeBitHint))
+ {
+ Id = NextFreeBitHint;
+ }
+ else
+ {
+ for (int Index = 0; Index < IdMasksCount; Index++)
+ {
+ int Mask = IdMasks[Index];
+
+ int FirstFreeBit = BitUtils.CountLeadingZeros32((Mask + 1) & ~Mask);
+
+ if (FirstFreeBit < 32)
+ {
+ int BaseBit = Index * 32 + 31;
+
+ Id = BaseBit - FirstFreeBit;
+
+ break;
+ }
+ else if (Index == IdMasksCount - 1)
+ {
+ throw new InvalidOperationException("Maximum number of Ids reached!");
+ }
+ }
+ }
+
+ NextFreeBitHint = Id + 1;
+
+ SetBit(Id);
+
+ return Id;
+ }
+ }
+
+ public void PutId(int Id)
+ {
+ lock (IdMasks)
+ {
+ ClearBit(Id);
+ }
+ }
+
+ private bool TestBit(int Bit)
+ {
+ return (IdMasks[NextFreeBitHint / 32] & (1 << (NextFreeBitHint & 31))) != 0;
+ }
+
+ private void SetBit(int Bit)
+ {
+ IdMasks[NextFreeBitHint / 32] |= (1 << (NextFreeBitHint & 31));
+ }
+
+ private void ClearBit(int Bit)
+ {
+ IdMasks[NextFreeBitHint / 32] &= ~(1 << (NextFreeBitHint & 31));
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs
index 02354e16..638dde9e 100644
--- a/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs
@@ -1,5 +1,4 @@
using Ryujinx.Common;
-using System;
namespace Ryujinx.HLE.HOS.Kernel
{
@@ -11,6 +10,10 @@ namespace Ryujinx.HLE.HOS.Kernel
public bool ContextSwitchNeeded { get; private set; }
+ public long LastContextSwitchTime { get; private set; }
+
+ public long TotalIdleTimeTicks { get; private set; } //TODO
+
public KThread CurrentThread { get; private set; }
public KThread SelectedThread { get; private set; }
@@ -24,11 +27,6 @@ namespace Ryujinx.HLE.HOS.Kernel
{
SelectedThread = Thread;
- if (Thread != null)
- {
- Thread.LastScheduledTicks = PerformanceCounter.ElapsedMilliseconds;
- }
-
if (SelectedThread != CurrentThread)
{
ContextSwitchNeeded = true;
@@ -39,25 +37,42 @@ namespace Ryujinx.HLE.HOS.Kernel
{
ContextSwitchNeeded = false;
+ LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
+
CurrentThread = SelectedThread;
+
+ if (CurrentThread != null)
+ {
+ long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
+
+ CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
+ CurrentThread.LastScheduledTime = CurrentTime;
+ }
}
public void ContextSwitch()
{
ContextSwitchNeeded = false;
+ LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
+
if (CurrentThread != null)
{
- CoreManager.GetThread(CurrentThread.Context.Work).Reset();
+ CoreManager.Reset(CurrentThread.Context.Work);
}
CurrentThread = SelectedThread;
if (CurrentThread != null)
{
+ long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
+
+ CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
+ CurrentThread.LastScheduledTime = CurrentTime;
+
CurrentThread.ClearExclusive();
- CoreManager.GetThread(CurrentThread.Context.Work).Set();
+ CoreManager.Set(CurrentThread.Context.Work);
CurrentThread.Context.Execute();
}
diff --git a/Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs b/Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs
index 30c1a880..b02a1195 100644
--- a/Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs
@@ -3,7 +3,7 @@ using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel
{
- class KRecursiveLock
+ class KCriticalSection
{
private Horizon System;
@@ -11,21 +11,21 @@ namespace Ryujinx.HLE.HOS.Kernel
private int RecursionCount;
- public KRecursiveLock(Horizon System)
+ public KCriticalSection(Horizon System)
{
this.System = System;
LockObj = new object();
}
- public void Lock()
+ public void Enter()
{
Monitor.Enter(LockObj);
RecursionCount++;
}
- public void Unlock()
+ public void Leave()
{
if (RecursionCount == 0)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs b/Ryujinx.HLE/HOS/Kernel/KHandleTable.cs
index 682f08d4..e39dfb67 100644
--- a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KHandleTable.cs
@@ -2,7 +2,7 @@ using System;
namespace Ryujinx.HLE.HOS.Kernel
{
- class KProcessHandleTable
+ class KHandleTable
{
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
@@ -20,12 +20,24 @@ namespace Ryujinx.HLE.HOS.Kernel
private ushort IdCounter;
- private object LockObj;
-
- public KProcessHandleTable(Horizon System, int Size = 1024)
+ public KHandleTable(Horizon System)
{
this.System = System;
- this.Size = Size;
+ }
+
+ public KernelResult Initialize(int Size)
+ {
+ if ((uint)Size > 1024)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ if (Size < 1)
+ {
+ Size = 1024;
+ }
+
+ this.Size = Size;
IdCounter = 1;
@@ -48,14 +60,14 @@ namespace Ryujinx.HLE.HOS.Kernel
NextFreeEntry = TableHead;
- LockObj = new object();
+ return KernelResult.Success;
}
public KernelResult GenerateHandle(object Obj, out int Handle)
{
Handle = 0;
- lock (LockObj)
+ lock (Table)
{
if (ActiveSlotsCount >= Size)
{
@@ -95,12 +107,12 @@ namespace Ryujinx.HLE.HOS.Kernel
return false;
}
- int Index = (Handle >> 0) & 0x7fff;
+ int Index = (Handle >> 0) & 0x7fff;
int HandleId = (Handle >> 15);
bool Result = false;
- lock (LockObj)
+ lock (Table)
{
if (HandleId != 0 && Index < Size)
{
@@ -125,10 +137,10 @@ namespace Ryujinx.HLE.HOS.Kernel
public T GetObject<T>(int Handle)
{
- int Index = (Handle >> 0) & 0x7fff;
+ int Index = (Handle >> 0) & 0x7fff;
int HandleId = (Handle >> 15);
- lock (LockObj)
+ lock (Table)
{
if ((Handle >> 30) == 0 && HandleId != 0)
{
@@ -156,9 +168,21 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
+ public KProcess GetKProcess(int Handle)
+ {
+ if (Handle == SelfProcessHandle)
+ {
+ return System.Scheduler.GetCurrentProcess();
+ }
+ else
+ {
+ return GetObject<KProcess>(Handle);
+ }
+ }
+
public void Destroy()
{
- lock (LockObj)
+ lock (Table)
{
for (int Index = 0; Index < Size; Index++)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs
new file mode 100644
index 00000000..af393b68
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs
@@ -0,0 +1,22 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KMemoryArrange
+ {
+ public KMemoryArrangeRegion Service { get; private set; }
+ public KMemoryArrangeRegion NvServices { get; private set; }
+ public KMemoryArrangeRegion Applet { get; private set; }
+ public KMemoryArrangeRegion Application { get; private set; }
+
+ public KMemoryArrange(
+ KMemoryArrangeRegion Service,
+ KMemoryArrangeRegion NvServices,
+ KMemoryArrangeRegion Applet,
+ KMemoryArrangeRegion Application)
+ {
+ this.Service = Service;
+ this.NvServices = NvServices;
+ this.Applet = Applet;
+ this.Application = Application;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs
new file mode 100644
index 00000000..7d66e291
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs
@@ -0,0 +1,16 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ struct KMemoryArrangeRegion
+ {
+ public ulong Address { get; private set; }
+ public ulong Size { get; private set; }
+
+ public ulong EndAddr => Address + Size;
+
+ public KMemoryArrangeRegion(ulong Address, ulong Size)
+ {
+ this.Address = Address;
+ this.Size = Size;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs
index 6100741b..08190236 100644
--- a/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs
@@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class KMemoryBlock
{
- public long BasePosition { get; set; }
- public long PagesCount { get; set; }
+ public ulong BaseAddress { get; set; }
+ public ulong PagesCount { get; set; }
public MemoryState State { get; set; }
public MemoryPermission Permission { get; set; }
@@ -13,25 +13,25 @@ namespace Ryujinx.HLE.HOS.Kernel
public int DeviceRefCount { get; set; }
public KMemoryBlock(
- long BasePosition,
- long PagesCount,
+ ulong BaseAddress,
+ ulong PagesCount,
MemoryState State,
MemoryPermission Permission,
MemoryAttribute Attribute)
{
- this.BasePosition = BasePosition;
- this.PagesCount = PagesCount;
- this.State = State;
- this.Attribute = Attribute;
- this.Permission = Permission;
+ this.BaseAddress = BaseAddress;
+ this.PagesCount = PagesCount;
+ this.State = State;
+ this.Attribute = Attribute;
+ this.Permission = Permission;
}
public KMemoryInfo GetInfo()
{
- long Size = PagesCount * KMemoryManager.PageSize;
+ ulong Size = PagesCount * KMemoryManager.PageSize;
return new KMemoryInfo(
- BasePosition,
+ BaseAddress,
Size,
State,
Permission,
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs
new file mode 100644
index 00000000..08512e12
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs
@@ -0,0 +1,19 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KMemoryBlockAllocator
+ {
+ private ulong CapacityElements;
+
+ public int Count { get; set; }
+
+ public KMemoryBlockAllocator(ulong CapacityElements)
+ {
+ this.CapacityElements = CapacityElements;
+ }
+
+ public bool CanAllocate(int Count)
+ {
+ return (ulong)(this.Count + Count) <= CapacityElements;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs
index 9b73b32b..09ba88f2 100644
--- a/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs
@@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class KMemoryInfo
{
- public long Position { get; private set; }
- public long Size { get; private set; }
+ public ulong Address { get; private set; }
+ public ulong Size { get; private set; }
public MemoryState State { get; private set; }
public MemoryPermission Permission { get; private set; }
@@ -13,15 +13,15 @@ namespace Ryujinx.HLE.HOS.Kernel
public int DeviceRefCount { get; private set; }
public KMemoryInfo(
- long Position,
- long Size,
+ ulong Address,
+ ulong Size,
MemoryState State,
MemoryPermission Permission,
MemoryAttribute Attribute,
int IpcRefCount,
int DeviceRefCount)
{
- this.Position = Position;
+ this.Address = Address;
this.Size = Size;
this.State = State;
this.Attribute = Attribute;
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
index 4cfb2aa2..0aa21e3f 100644
--- a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
@@ -1,331 +1,858 @@
using ChocolArm64.Memory;
-using Ryujinx.HLE.Memory;
+using Ryujinx.Common;
using System;
using System.Collections.Generic;
-using static Ryujinx.HLE.HOS.ErrorCode;
-
namespace Ryujinx.HLE.HOS.Kernel
{
class KMemoryManager
{
public const int PageSize = 0x1000;
+ private const int KMemoryBlockSize = 0x40;
+
+ //We need 2 blocks for the case where a big block
+ //needs to be split in 2, plus one block that will be the new one inserted.
+ private const int MaxBlocksNeededForInsertion = 2;
+
private LinkedList<KMemoryBlock> Blocks;
private MemoryManager CpuMemory;
- private ArenaAllocator Allocator;
+ private Horizon System;
+
+ public ulong AddrSpaceStart { get; private set; }
+ public ulong AddrSpaceEnd { get; private set; }
+
+ public ulong CodeRegionStart { get; private set; }
+ public ulong CodeRegionEnd { get; private set; }
+
+ public ulong HeapRegionStart { get; private set; }
+ public ulong HeapRegionEnd { get; private set; }
+
+ private ulong CurrentHeapAddr;
- public long AddrSpaceStart { get; private set; }
- public long AddrSpaceEnd { get; private set; }
+ public ulong AliasRegionStart { get; private set; }
+ public ulong AliasRegionEnd { get; private set; }
- public long CodeRegionStart { get; private set; }
- public long CodeRegionEnd { get; private set; }
+ public ulong StackRegionStart { get; private set; }
+ public ulong StackRegionEnd { get; private set; }
- public long MapRegionStart { get; private set; }
- public long MapRegionEnd { get; private set; }
+ public ulong TlsIoRegionStart { get; private set; }
+ public ulong TlsIoRegionEnd { get; private set; }
- public long HeapRegionStart { get; private set; }
- public long HeapRegionEnd { get; private set; }
+ private ulong HeapCapacity;
- public long NewMapRegionStart { get; private set; }
- public long NewMapRegionEnd { get; private set; }
+ public ulong PhysicalMemoryUsage { get; private set; }
- public long TlsIoRegionStart { get; private set; }
- public long TlsIoRegionEnd { get; private set; }
+ private MemoryRegion MemRegion;
- public long PersonalMmHeapUsage { get; private set; }
+ private bool AslrDisabled;
- private long CurrentHeapAddr;
+ public int AddrSpaceWidth { get; private set; }
- public KMemoryManager(Process Process)
+ private bool IsKernel;
+ private bool AslrEnabled;
+
+ private KMemoryBlockAllocator BlockAllocator;
+
+ private int ContextId;
+
+ private MersenneTwister RandomNumberGenerator;
+
+ public KMemoryManager(Horizon System, MemoryManager CpuMemory)
{
- CpuMemory = Process.Memory;
- Allocator = Process.Device.Memory.Allocator;
+ this.System = System;
+ this.CpuMemory = CpuMemory;
- long CodeRegionSize;
- long MapRegionSize;
- long HeapRegionSize;
- long NewMapRegionSize;
- long TlsIoRegionSize;
- int AddrSpaceWidth;
+ Blocks = new LinkedList<KMemoryBlock>();
+ }
- AddressSpaceType AddrType = AddressSpaceType.Addr39Bits;
+ private static readonly int[] AddrSpaceSizes = new int[] { 32, 36, 32, 39 };
- if (Process.MetaData != null)
+ public KernelResult InitializeForProcess(
+ AddressSpaceType AddrSpaceType,
+ bool AslrEnabled,
+ bool AslrDisabled,
+ MemoryRegion MemRegion,
+ ulong Address,
+ ulong Size,
+ KMemoryBlockAllocator BlockAllocator)
+ {
+ if ((uint)AddrSpaceType > (uint)AddressSpaceType.Addr39Bits)
{
- AddrType = (AddressSpaceType)Process.MetaData.AddressSpaceWidth;
+ throw new ArgumentException(nameof(AddrSpaceType));
}
- switch (AddrType)
+ ContextId = System.ContextIdManager.GetId();
+
+ ulong AddrSpaceBase = 0;
+ ulong AddrSpaceSize = 1UL << AddrSpaceSizes[(int)AddrSpaceType];
+
+ KernelResult Result = CreateUserAddressSpace(
+ AddrSpaceType,
+ AslrEnabled,
+ AslrDisabled,
+ AddrSpaceBase,
+ AddrSpaceSize,
+ MemRegion,
+ Address,
+ Size,
+ BlockAllocator);
+
+ if (Result != KernelResult.Success)
+ {
+ System.ContextIdManager.PutId(ContextId);
+ }
+
+ return Result;
+ }
+
+ private class Region
+ {
+ public ulong Start;
+ public ulong End;
+ public ulong Size;
+ public ulong AslrOffset;
+ }
+
+ private KernelResult CreateUserAddressSpace(
+ AddressSpaceType AddrSpaceType,
+ bool AslrEnabled,
+ bool AslrDisabled,
+ ulong AddrSpaceStart,
+ ulong AddrSpaceEnd,
+ MemoryRegion MemRegion,
+ ulong Address,
+ ulong Size,
+ KMemoryBlockAllocator BlockAllocator)
+ {
+ ulong EndAddr = Address + Size;
+
+ Region AliasRegion = new Region();
+ Region HeapRegion = new Region();
+ Region StackRegion = new Region();
+ Region TlsIoRegion = new Region();
+
+ ulong CodeRegionSize;
+ ulong StackAndTlsIoStart;
+ ulong StackAndTlsIoEnd;
+ ulong BaseAddress;
+
+ switch (AddrSpaceType)
{
case AddressSpaceType.Addr32Bits:
- CodeRegionStart = 0x200000;
- CodeRegionSize = 0x3fe00000;
- MapRegionSize = 0x40000000;
- HeapRegionSize = 0x40000000;
- NewMapRegionSize = 0;
- TlsIoRegionSize = 0;
- AddrSpaceWidth = 32;
+ AliasRegion.Size = 0x40000000;
+ HeapRegion.Size = 0x40000000;
+ StackRegion.Size = 0;
+ TlsIoRegion.Size = 0;
+ CodeRegionStart = 0x200000;
+ CodeRegionSize = 0x3fe00000;
+ StackAndTlsIoStart = 0x200000;
+ StackAndTlsIoEnd = 0x40000000;
+ BaseAddress = 0x200000;
+ AddrSpaceWidth = 32;
break;
case AddressSpaceType.Addr36Bits:
- CodeRegionStart = 0x8000000;
- CodeRegionSize = 0x78000000;
- MapRegionSize = 0x180000000;
- HeapRegionSize = 0x180000000;
- NewMapRegionSize = 0;
- TlsIoRegionSize = 0;
- AddrSpaceWidth = 36;
+ AliasRegion.Size = 0x180000000;
+ HeapRegion.Size = 0x180000000;
+ StackRegion.Size = 0;
+ TlsIoRegion.Size = 0;
+ CodeRegionStart = 0x8000000;
+ CodeRegionSize = 0x78000000;
+ StackAndTlsIoStart = 0x8000000;
+ StackAndTlsIoEnd = 0x80000000;
+ BaseAddress = 0x8000000;
+ AddrSpaceWidth = 36;
break;
- case AddressSpaceType.Addr36BitsNoMap:
- CodeRegionStart = 0x200000;
- CodeRegionSize = 0x3fe00000;
- MapRegionSize = 0;
- HeapRegionSize = 0x80000000;
- NewMapRegionSize = 0;
- TlsIoRegionSize = 0;
- AddrSpaceWidth = 36;
+ case AddressSpaceType.Addr32BitsNoMap:
+ AliasRegion.Size = 0;
+ HeapRegion.Size = 0x80000000;
+ StackRegion.Size = 0;
+ TlsIoRegion.Size = 0;
+ CodeRegionStart = 0x200000;
+ CodeRegionSize = 0x3fe00000;
+ StackAndTlsIoStart = 0x200000;
+ StackAndTlsIoEnd = 0x40000000;
+ BaseAddress = 0x200000;
+ AddrSpaceWidth = 32;
break;
case AddressSpaceType.Addr39Bits:
- CodeRegionStart = 0x8000000;
- CodeRegionSize = 0x80000000;
- MapRegionSize = 0x1000000000;
- HeapRegionSize = 0x180000000;
- NewMapRegionSize = 0x80000000;
- TlsIoRegionSize = 0x1000000000;
- AddrSpaceWidth = 39;
+ AliasRegion.Size = 0x1000000000;
+ HeapRegion.Size = 0x180000000;
+ StackRegion.Size = 0x80000000;
+ TlsIoRegion.Size = 0x1000000000;
+ CodeRegionStart = BitUtils.AlignDown(Address, 0x200000);
+ CodeRegionSize = BitUtils.AlignUp (EndAddr, 0x200000) - CodeRegionStart;
+ StackAndTlsIoStart = 0;
+ StackAndTlsIoEnd = 0;
+ BaseAddress = 0x8000000;
+ AddrSpaceWidth = 39;
break;
- default: throw new InvalidOperationException();
+ default: throw new ArgumentException(nameof(AddrSpaceType));
+ }
+
+ CodeRegionEnd = CodeRegionStart + CodeRegionSize;
+
+ ulong MapBaseAddress;
+ ulong MapAvailableSize;
+
+ if (CodeRegionStart - BaseAddress >= AddrSpaceEnd - CodeRegionEnd)
+ {
+ //Has more space before the start of the code region.
+ MapBaseAddress = BaseAddress;
+ MapAvailableSize = CodeRegionStart - BaseAddress;
+ }
+ else
+ {
+ //Has more space after the end of the code region.
+ MapBaseAddress = CodeRegionEnd;
+ MapAvailableSize = AddrSpaceEnd - CodeRegionEnd;
}
- AddrSpaceStart = 0;
- AddrSpaceEnd = 1L << AddrSpaceWidth;
+ ulong MapTotalSize = AliasRegion.Size + HeapRegion.Size + StackRegion.Size + TlsIoRegion.Size;
+
+ ulong AslrMaxOffset = MapAvailableSize - MapTotalSize;
- CodeRegionEnd = CodeRegionStart + CodeRegionSize;
- MapRegionStart = CodeRegionEnd;
- MapRegionEnd = CodeRegionEnd + MapRegionSize;
- HeapRegionStart = MapRegionEnd;
- HeapRegionEnd = MapRegionEnd + HeapRegionSize;
- NewMapRegionStart = HeapRegionEnd;
- NewMapRegionEnd = HeapRegionEnd + NewMapRegionSize;
- TlsIoRegionStart = NewMapRegionEnd;
- TlsIoRegionEnd = NewMapRegionEnd + TlsIoRegionSize;
+ this.AslrEnabled = AslrEnabled;
- CurrentHeapAddr = HeapRegionStart;
+ this.AddrSpaceStart = AddrSpaceStart;
+ this.AddrSpaceEnd = AddrSpaceEnd;
- if (NewMapRegionSize == 0)
+ this.BlockAllocator = BlockAllocator;
+
+ if (MapAvailableSize < MapTotalSize)
{
- NewMapRegionStart = AddrSpaceStart;
- NewMapRegionEnd = AddrSpaceEnd;
+ return KernelResult.OutOfMemory;
}
- Blocks = new LinkedList<KMemoryBlock>();
+ if (AslrEnabled)
+ {
+ AliasRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ HeapRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ StackRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ TlsIoRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ }
+
+ //Regions are sorted based on ASLR offset.
+ //When ASLR is disabled, the order is Map, Heap, NewMap and TlsIo.
+ AliasRegion.Start = MapBaseAddress + AliasRegion.AslrOffset;
+ AliasRegion.End = AliasRegion.Start + AliasRegion.Size;
+ HeapRegion.Start = MapBaseAddress + HeapRegion.AslrOffset;
+ HeapRegion.End = HeapRegion.Start + HeapRegion.Size;
+ StackRegion.Start = MapBaseAddress + StackRegion.AslrOffset;
+ StackRegion.End = StackRegion.Start + StackRegion.Size;
+ TlsIoRegion.Start = MapBaseAddress + TlsIoRegion.AslrOffset;
+ TlsIoRegion.End = TlsIoRegion.Start + TlsIoRegion.Size;
- long AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
+ SortRegion(HeapRegion, AliasRegion);
+
+ if (StackRegion.Size != 0)
+ {
+ SortRegion(StackRegion, AliasRegion);
+ SortRegion(StackRegion, HeapRegion);
+ }
+ else
+ {
+ StackRegion.Start = StackAndTlsIoStart;
+ StackRegion.End = StackAndTlsIoEnd;
+ }
+
+ if (TlsIoRegion.Size != 0)
+ {
+ SortRegion(TlsIoRegion, AliasRegion);
+ SortRegion(TlsIoRegion, HeapRegion);
+ SortRegion(TlsIoRegion, StackRegion);
+ }
+ else
+ {
+ TlsIoRegion.Start = StackAndTlsIoStart;
+ TlsIoRegion.End = StackAndTlsIoEnd;
+ }
+
+ AliasRegionStart = AliasRegion.Start;
+ AliasRegionEnd = AliasRegion.End;
+ HeapRegionStart = HeapRegion.Start;
+ HeapRegionEnd = HeapRegion.End;
+ StackRegionStart = StackRegion.Start;
+ StackRegionEnd = StackRegion.End;
+ TlsIoRegionStart = TlsIoRegion.Start;
+ TlsIoRegionEnd = TlsIoRegion.End;
+
+ CurrentHeapAddr = HeapRegionStart;
+ HeapCapacity = 0;
+ PhysicalMemoryUsage = 0;
+
+ this.MemRegion = MemRegion;
+ this.AslrDisabled = AslrDisabled;
+
+ return InitializeBlocks(AddrSpaceStart, AddrSpaceEnd);
+ }
+
+ private ulong GetRandomValue(ulong Min, ulong Max)
+ {
+ return (ulong)GetRandomValue((long)Min, (long)Max);
+ }
+
+ private long GetRandomValue(long Min, long Max)
+ {
+ if (RandomNumberGenerator == null)
+ {
+ RandomNumberGenerator = new MersenneTwister(0);
+ }
+
+ return RandomNumberGenerator.GenRandomNumber(Min, Max);
+ }
+
+ private static void SortRegion(Region Lhs, Region Rhs)
+ {
+ if (Lhs.AslrOffset < Rhs.AslrOffset)
+ {
+ Rhs.Start += Lhs.Size;
+ Rhs.End += Lhs.Size;
+ }
+ else
+ {
+ Lhs.Start += Rhs.Size;
+ Lhs.End += Rhs.Size;
+ }
+ }
+
+ private KernelResult InitializeBlocks(ulong AddrSpaceStart, ulong AddrSpaceEnd)
+ {
+ //First insertion will always need only a single block,
+ //because there's nothing else to split.
+ if (!BlockAllocator.CanAllocate(1))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
InsertBlock(AddrSpaceStart, AddrSpacePagesCount, MemoryState.Unmapped);
+
+ return KernelResult.Success;
}
- public void HleMapProcessCode(long Position, long Size)
+ public KernelResult MapPages(
+ ulong Address,
+ KPageList PageList,
+ MemoryState State,
+ MemoryPermission Permission)
{
- long PagesCount = Size / PageSize;
+ ulong PagesCount = PageList.GetPagesCount();
+
+ ulong Size = PagesCount * PageSize;
- if (!Allocator.TryAllocate(Size, out long PA))
+ if (!ValidateRegionForState(Address, Size, State))
{
- throw new InvalidOperationException();
+ return KernelResult.InvalidMemState;
}
lock (Blocks)
{
- InsertBlock(Position, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute);
+ if (!IsUnmapped(Address, PagesCount * PageSize))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ KernelResult Result = MapPages(Address, PageList, Permission);
+
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, State, Permission);
+ }
- CpuMemory.Map(Position, PA, Size);
+ return Result;
}
}
- public long MapProcessCodeMemory(long Dst, long Src, long Size)
+ public KernelResult UnmapPages(ulong Address, KPageList PageList, MemoryState StateExpected)
{
+ ulong PagesCount = PageList.GetPagesCount();
+
+ ulong Size = PagesCount * PageSize;
+
+ ulong EndAddr = Address + Size;
+
+ ulong AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
+
+ if (AddrSpaceStart > Address)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (AddrSpacePagesCount < PagesCount)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (EndAddr - 1 > AddrSpaceEnd - 1)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
lock (Blocks)
{
- long PagesCount = Size / PageSize;
+ KPageList CurrentPageList = new KPageList();
- bool Success = IsUnmapped(Dst, Size);
+ AddVaRangeToPageList(CurrentPageList, Address, PagesCount);
- Success &= CheckRange(
- Src,
- Size,
- MemoryState.Mask,
- MemoryState.Heap,
- MemoryPermission.Mask,
- MemoryPermission.ReadAndWrite,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _);
+ if (!CurrentPageList.IsEqual(PageList))
+ {
+ return KernelResult.InvalidMemRange;
+ }
- if (Success)
+ if (CheckRange(
+ Address,
+ Size,
+ MemoryState.Mask,
+ StateExpected,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out MemoryState State,
+ out _,
+ out _))
{
- long PA = CpuMemory.GetPhysicalAddress(Src);
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
- InsertBlock(Dst, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute);
- InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.None);
+ KernelResult Result = MmuUnmap(Address, PagesCount);
- CpuMemory.Map(Dst, PA, Size);
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+ }
- return 0;
+ return Result;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
+ }
+
+ public KernelResult MapNormalMemory(long Address, long Size, MemoryPermission Permission)
+ {
+ //TODO.
+ return KernelResult.Success;
+ }
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ public KernelResult MapIoMemory(long Address, long Size, MemoryPermission Permission)
+ {
+ //TODO.
+ return KernelResult.Success;
}
- public long UnmapProcessCodeMemory(long Dst, long Src, long Size)
+ public KernelResult AllocateOrMapPa(
+ ulong NeededPagesCount,
+ int Alignment,
+ ulong SrcPa,
+ bool Map,
+ ulong RegionStart,
+ ulong RegionPagesCount,
+ MemoryState State,
+ MemoryPermission Permission,
+ out ulong Address)
{
+ Address = 0;
+
+ ulong RegionSize = RegionPagesCount * PageSize;
+
+ ulong RegionEndAddr = RegionStart + RegionSize;
+
+ if (!ValidateRegionForState(RegionStart, RegionSize, State))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (RegionPagesCount <= NeededPagesCount)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ ulong ReservedPagesCount = IsKernel ? 1UL : 4UL;
+
lock (Blocks)
{
- long PagesCount = Size / PageSize;
+ if (AslrEnabled)
+ {
+ ulong TotalNeededSize = (ReservedPagesCount + NeededPagesCount) * PageSize;
- bool Success = CheckRange(
- Dst,
- Size,
- MemoryState.Mask,
- MemoryState.CodeStatic,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _);
+ ulong RemainingPages = RegionPagesCount - NeededPagesCount;
- Success &= CheckRange(
- Src,
- Size,
- MemoryState.Mask,
- MemoryState.Heap,
- MemoryPermission.Mask,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _);
+ ulong AslrMaxOffset = ((RemainingPages + ReservedPagesCount) * PageSize) / (ulong)Alignment;
- if (Success)
+ for (int Attempt = 0; Attempt < 8; Attempt++)
+ {
+ Address = BitUtils.AlignDown(RegionStart + GetRandomValue(0, AslrMaxOffset) * (ulong)Alignment, Alignment);
+
+ ulong EndAddr = Address + TotalNeededSize;
+
+ KMemoryInfo Info = FindBlock(Address).GetInfo();
+
+ if (Info.State != MemoryState.Unmapped)
+ {
+ continue;
+ }
+
+ ulong CurrBaseAddr = Info.Address + ReservedPagesCount * PageSize;
+ ulong CurrEndAddr = Info.Address + Info.Size;
+
+ if (Address >= RegionStart &&
+ Address >= CurrBaseAddr &&
+ EndAddr - 1 <= RegionEndAddr - 1 &&
+ EndAddr - 1 <= CurrEndAddr - 1)
+ {
+ break;
+ }
+ }
+
+ if (Address == 0)
+ {
+ ulong AslrPage = GetRandomValue(0, AslrMaxOffset);
+
+ Address = FindFirstFit(
+ RegionStart + AslrPage * PageSize,
+ RegionPagesCount - AslrPage,
+ NeededPagesCount,
+ Alignment,
+ 0,
+ ReservedPagesCount);
+ }
+ }
+
+ if (Address == 0)
{
- InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
- InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
+ Address = FindFirstFit(
+ RegionStart,
+ RegionPagesCount,
+ NeededPagesCount,
+ Alignment,
+ 0,
+ ReservedPagesCount);
+ }
- CpuMemory.Unmap(Dst, Size);
+ if (Address == 0)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ MemoryOperation Operation = Map
+ ? MemoryOperation.MapPa
+ : MemoryOperation.Allocate;
- return 0;
+ KernelResult Result = DoMmuOperation(
+ Address,
+ NeededPagesCount,
+ SrcPa,
+ Map,
+ Permission,
+ Operation);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
}
+
+ InsertBlock(Address, NeededPagesCount, State, Permission);
}
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ return KernelResult.Success;
}
- public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission)
+ public KernelResult MapNewProcessCode(
+ ulong Address,
+ ulong PagesCount,
+ MemoryState State,
+ MemoryPermission Permission)
{
- long PagesCount = Size / PageSize;
+ ulong Size = PagesCount * PageSize;
- if (!Allocator.TryAllocate(Size, out long PA))
+ if (!ValidateRegionForState(Address, Size, State))
{
- throw new InvalidOperationException();
+ return KernelResult.InvalidMemState;
}
lock (Blocks)
{
- InsertBlock(Position, PagesCount, State, Permission);
+ if (!IsUnmapped(Address, Size))
+ {
+ return KernelResult.InvalidMemState;
+ }
- CpuMemory.Map(Position, PA, Size);
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ KernelResult Result = DoMmuOperation(
+ Address,
+ PagesCount,
+ 0,
+ false,
+ Permission,
+ MemoryOperation.Allocate);
+
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, State, Permission);
+ }
+
+ return Result;
}
}
- public long HleMapTlsPage()
+ public KernelResult MapProcessCodeMemory(ulong Dst, ulong Src, ulong Size)
{
- bool HasTlsIoRegion = TlsIoRegionStart != TlsIoRegionEnd;
-
- long Position = HasTlsIoRegion ? TlsIoRegionStart : CodeRegionStart;
+ ulong PagesCount = Size / PageSize;
lock (Blocks)
{
- while (Position < (HasTlsIoRegion ? TlsIoRegionEnd : CodeRegionEnd))
+ bool Success = CheckRange(
+ Src,
+ Size,
+ MemoryState.Mask,
+ MemoryState.Heap,
+ MemoryPermission.Mask,
+ MemoryPermission.ReadAndWrite,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out MemoryState State,
+ out MemoryPermission Permission,
+ out _);
+
+ Success &= IsUnmapped(Dst, Size);
+
+ if (Success)
{
- if (FindBlock(Position).State == MemoryState.Unmapped)
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
{
- InsertBlock(Position, 1, MemoryState.ThreadLocal, MemoryPermission.ReadAndWrite);
+ return KernelResult.OutOfResource;
+ }
- if (!Allocator.TryAllocate(PageSize, out long PA))
- {
- throw new InvalidOperationException();
- }
+ KPageList PageList = new KPageList();
+
+ AddVaRangeToPageList(PageList, Src, PagesCount);
- CpuMemory.Map(Position, PA, PageSize);
+ KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Result = MapPages(Dst, PageList, MemoryPermission.None);
+
+ if (Result != KernelResult.Success)
+ {
+ MmuChangePermission(Src, PagesCount, Permission);
- return Position;
+ return Result;
}
- Position += PageSize;
+ InsertBlock(Src, PagesCount, State, MemoryPermission.None, MemoryAttribute.Borrowed);
+ InsertBlock(Dst, PagesCount, MemoryState.ModCodeStatic);
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
+ }
+ }
+
+ public KernelResult UnmapProcessCodeMemory(ulong Dst, ulong Src, ulong Size)
+ {
+ ulong PagesCount = Size / PageSize;
+
+ lock (Blocks)
+ {
+ bool Success = CheckRange(
+ Src,
+ Size,
+ MemoryState.Mask,
+ MemoryState.Heap,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.Borrowed,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out _,
+ out _,
+ out _);
- throw new InvalidOperationException();
+ Success &= CheckRange(
+ Dst,
+ PageSize,
+ MemoryState.UnmapProcessCodeMemoryAllowed,
+ MemoryState.UnmapProcessCodeMemoryAllowed,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out MemoryState State,
+ out _,
+ out _);
+
+ Success &= CheckRange(
+ Dst,
+ Size,
+ MemoryState.Mask,
+ State,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None);
+
+ if (Success)
+ {
+ KernelResult Result = MmuUnmap(Dst, PagesCount);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ //TODO: Missing some checks here.
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
+ InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
+ }
}
}
- public long TrySetHeapSize(long Size, out long Position)
+ public KernelResult SetHeapSize(ulong Size, out ulong Address)
{
- Position = 0;
+ Address = 0;
- if ((ulong)Size > (ulong)(HeapRegionEnd - HeapRegionStart))
+ if (Size > HeapRegionEnd - HeapRegionStart)
{
- return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+ return KernelResult.OutOfMemory;
}
- bool Success = false;
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- long CurrentHeapSize = GetHeapSize();
+ ulong CurrentHeapSize = GetHeapSize();
- if ((ulong)CurrentHeapSize <= (ulong)Size)
+ if (CurrentHeapSize <= Size)
{
//Expand.
- long DiffSize = Size - CurrentHeapSize;
+ ulong DiffSize = Size - CurrentHeapSize;
lock (Blocks)
{
- if (Success = IsUnmapped(CurrentHeapAddr, DiffSize))
+ if (CurrentProcess.ResourceLimit != null && DiffSize != 0 &&
+ !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Memory, DiffSize))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+
+ ulong PagesCount = DiffSize / PageSize;
+
+ KMemoryRegionManager Region = GetMemoryRegionManager();
+
+ KernelResult Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList);
+
+ void CleanUpForError()
{
- if (!Allocator.TryAllocate(DiffSize, out long PA))
+ if (PageList != null)
+ {
+ Region.FreePages(PageList);
+ }
+
+ if (CurrentProcess.ResourceLimit != null && DiffSize != 0)
{
- return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+ CurrentProcess.ResourceLimit.Release(LimitableResource.Memory, DiffSize);
}
+ }
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
- long PagesCount = DiffSize / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ CleanUpForError();
- InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
+ return KernelResult.OutOfResource;
+ }
- CpuMemory.Map(CurrentHeapAddr, PA, DiffSize);
+ if (!IsUnmapped(CurrentHeapAddr, DiffSize))
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidMemState;
}
+
+ Result = DoMmuOperation(
+ CurrentHeapAddr,
+ PagesCount,
+ PageList,
+ MemoryPermission.ReadAndWrite,
+ MemoryOperation.MapVa);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
}
}
else
{
//Shrink.
- long FreeAddr = HeapRegionStart + Size;
- long DiffSize = CurrentHeapSize - Size;
+ ulong FreeAddr = HeapRegionStart + Size;
+ ulong DiffSize = CurrentHeapSize - Size;
lock (Blocks)
{
- Success = CheckRange(
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ if (!CheckRange(
FreeAddr,
DiffSize,
MemoryState.Mask,
@@ -337,48 +864,66 @@ namespace Ryujinx.HLE.HOS.Kernel
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
- out _);
-
- if (Success)
+ out _))
{
- long PagesCount = DiffSize / PageSize;
+ return KernelResult.InvalidMemState;
+ }
- InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped);
+ ulong PagesCount = DiffSize / PageSize;
- FreePages(FreeAddr, PagesCount);
+ KernelResult Result = MmuUnmap(FreeAddr, PagesCount);
- CpuMemory.Unmap(FreeAddr, DiffSize);
+ if (Result != KernelResult.Success)
+ {
+ return Result;
}
+
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, BitUtils.AlignDown(DiffSize, PageSize));
+
+ InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped);
}
}
CurrentHeapAddr = HeapRegionStart + Size;
- if (Success)
- {
- Position = HeapRegionStart;
+ Address = HeapRegionStart;
- return 0;
- }
+ return KernelResult.Success;
+ }
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ public ulong GetTotalHeapSize()
+ {
+ lock (Blocks)
+ {
+ return GetHeapSize() + PhysicalMemoryUsage;
+ }
}
- public long GetHeapSize()
+ private ulong GetHeapSize()
{
return CurrentHeapAddr - HeapRegionStart;
}
- public long SetMemoryAttribute(
- long Position,
- long Size,
+ public KernelResult SetHeapCapacity(ulong Capacity)
+ {
+ lock (Blocks)
+ {
+ HeapCapacity = Capacity;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult SetMemoryAttribute(
+ ulong Address,
+ ulong Size,
MemoryAttribute AttributeMask,
MemoryAttribute AttributeValue)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.AttributeChangeAllowed,
MemoryState.AttributeChangeAllowed,
@@ -391,35 +936,42 @@ namespace Ryujinx.HLE.HOS.Kernel
out MemoryPermission Permission,
out MemoryAttribute Attribute))
{
- long PagesCount = Size / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong PagesCount = Size / PageSize;
Attribute &= ~AttributeMask;
Attribute |= AttributeMask & AttributeValue;
- InsertBlock(Position, PagesCount, State, Permission, Attribute);
+ InsertBlock(Address, PagesCount, State, Permission, Attribute);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public KMemoryInfo QueryMemory(long Position)
+ public KMemoryInfo QueryMemory(ulong Address)
{
- if ((ulong)Position >= (ulong)AddrSpaceStart &&
- (ulong)Position < (ulong)AddrSpaceEnd)
+ if (Address >= AddrSpaceStart &&
+ Address < AddrSpaceEnd)
{
lock (Blocks)
{
- return FindBlock(Position).GetInfo();
+ return FindBlock(Address).GetInfo();
}
}
else
{
return new KMemoryInfo(
AddrSpaceEnd,
- -AddrSpaceEnd,
+ ~AddrSpaceEnd + 1,
MemoryState.Reserved,
MemoryPermission.None,
MemoryAttribute.None,
@@ -428,7 +980,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- public long Map(long Src, long Dst, long Size)
+ public KernelResult Map(ulong Dst, ulong Src, ulong Size)
{
bool Success;
@@ -452,22 +1004,90 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Success)
{
- long PagesCount = Size / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+ {
+ return KernelResult.OutOfResource;
+ }
- InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed);
+ ulong PagesCount = Size / PageSize;
- InsertBlock(Dst, PagesCount, MemoryState.MappedMemory, MemoryPermission.ReadAndWrite);
+ KPageList PageList = new KPageList();
- long PA = CpuMemory.GetPhysicalAddress(Src);
+ AddVaRangeToPageList(PageList, Src, PagesCount);
- CpuMemory.Map(Dst, PA, Size);
+ KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Result = MapPages(Dst, PageList, MemoryPermission.ReadAndWrite);
+
+ if (Result != KernelResult.Success)
+ {
+ if (MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure reverting memory permission.");
+ }
+
+ return Result;
+ }
+
+ InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed);
+ InsertBlock(Dst, PagesCount, MemoryState.Stack, MemoryPermission.ReadAndWrite);
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
+ }
+
+ public KernelResult UnmapForKernel(ulong Address, ulong PagesCount, MemoryState StateExpected)
+ {
+ ulong Size = PagesCount * PageSize;
+
+ lock (Blocks)
+ {
+ if (CheckRange(
+ Address,
+ Size,
+ MemoryState.Mask,
+ StateExpected,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out _,
+ out _,
+ out _))
+ {
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ KernelResult Result = MmuUnmap(Address, PagesCount);
- return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+ }
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
+ }
+ }
}
- public long Unmap(long Src, long Dst, long Size)
+ public KernelResult Unmap(ulong Dst, ulong Src, ulong Size)
{
bool Success;
@@ -491,87 +1111,70 @@ namespace Ryujinx.HLE.HOS.Kernel
Dst,
Size,
MemoryState.Mask,
- MemoryState.MappedMemory,
+ MemoryState.Stack,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
- out _,
+ out MemoryPermission DstPermission,
out _);
if (Success)
{
- long PagesCount = Size / PageSize;
-
- InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite);
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+ {
+ return KernelResult.OutOfResource;
+ }
- InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
+ ulong PagesCount = Size / PageSize;
- CpuMemory.Unmap(Dst, Size);
- }
- }
+ KPageList SrcPageList = new KPageList();
+ KPageList DstPageList = new KPageList();
- return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
- }
+ AddVaRangeToPageList(SrcPageList, Src, PagesCount);
+ AddVaRangeToPageList(DstPageList, Dst, PagesCount);
- public long MapSharedMemory(KSharedMemory SharedMemory, MemoryPermission Permission, long Position)
- {
- lock (Blocks)
- {
- if (IsUnmapped(Position, SharedMemory.Size))
- {
- long PagesCount = SharedMemory.Size / PageSize;
-
- InsertBlock(Position, PagesCount, MemoryState.SharedMemory, Permission);
+ if (!DstPageList.IsEqual(SrcPageList))
+ {
+ return KernelResult.InvalidMemRange;
+ }
- CpuMemory.Map(Position, SharedMemory.PA, SharedMemory.Size);
+ KernelResult Result = MmuUnmap(Dst, PagesCount);
- return 0;
- }
- }
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
- }
+ Result = MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite);
- public long UnmapSharedMemory(long Position, long Size)
- {
- lock (Blocks)
- {
- if (CheckRange(
- Position,
- Size,
- MemoryState.Mask,
- MemoryState.SharedMemory,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState State,
- out _,
- out _))
- {
- long PagesCount = Size / PageSize;
+ if (Result != KernelResult.Success)
+ {
+ MapPages(Dst, DstPageList, DstPermission);
- InsertBlock(Position, PagesCount, MemoryState.Unmapped);
+ return Result;
+ }
- CpuMemory.Unmap(Position, Size);
+ InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite);
+ InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long ReserveTransferMemory(long Position, long Size, MemoryPermission Permission)
+ public KernelResult ReserveTransferMemory(ulong Address, ulong Size, MemoryPermission Permission)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
@@ -584,25 +1187,34 @@ namespace Ryujinx.HLE.HOS.Kernel
out _,
out MemoryAttribute Attribute))
{
- long PagesCount = Size / PageSize;
+ //TODO: Missing checks.
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong PagesCount = Size / PageSize;
Attribute |= MemoryAttribute.Borrowed;
- InsertBlock(Position, PagesCount, State, Permission, Attribute);
+ InsertBlock(Address, PagesCount, State, Permission, Attribute);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long ResetTransferMemory(long Position, long Size)
+ public KernelResult ResetTransferMemory(ulong Address, ulong Size)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
@@ -615,23 +1227,30 @@ namespace Ryujinx.HLE.HOS.Kernel
out _,
out _))
{
- long PagesCount = Size / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong PagesCount = Size / PageSize;
- InsertBlock(Position, PagesCount, State, MemoryPermission.ReadAndWrite);
+ InsertBlock(Address, PagesCount, State, MemoryPermission.ReadAndWrite);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long SetProcessMemoryPermission(long Position, long Size, MemoryPermission Permission)
+ public KernelResult SetProcessMemoryPermission(ulong Address, ulong Size, MemoryPermission Permission)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.ProcessPermissionChangeAllowed,
MemoryState.ProcessPermissionChangeAllowed,
@@ -640,47 +1259,73 @@ namespace Ryujinx.HLE.HOS.Kernel
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState State,
- out _,
+ out MemoryState OldState,
+ out MemoryPermission OldPermission,
out _))
{
- if (State == MemoryState.CodeStatic)
- {
- State = MemoryState.CodeMutable;
- }
- else if (State == MemoryState.ModCodeStatic)
+ MemoryState NewState = OldState;
+
+ //If writing into the code region is allowed, then we need
+ //to change it to mutable.
+ if ((Permission & MemoryPermission.Write) != 0)
{
- State = MemoryState.ModCodeMutable;
+ if (OldState == MemoryState.CodeStatic)
+ {
+ NewState = MemoryState.CodeMutable;
+ }
+ else if (OldState == MemoryState.ModCodeStatic)
+ {
+ NewState = MemoryState.ModCodeMutable;
+ }
+ else
+ {
+ throw new InvalidOperationException($"Memory state \"{OldState}\" not valid for this operation.");
+ }
}
- else
+
+ if (NewState != OldState || Permission != OldPermission)
{
- throw new InvalidOperationException();
- }
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
- long PagesCount = Size / PageSize;
+ ulong PagesCount = Size / PageSize;
- InsertBlock(Position, PagesCount, State, Permission);
+ MemoryOperation Operation = (Permission & MemoryPermission.Execute) != 0
+ ? MemoryOperation.ChangePermsAndAttributes
+ : MemoryOperation.ChangePermRw;
- return 0;
+ KernelResult Result = DoMmuOperation(Address, PagesCount, 0, false, Permission, Operation);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ InsertBlock(Address, PagesCount, NewState, Permission);
+ }
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long MapPhysicalMemory(long Position, long Size)
+ public KernelResult MapPhysicalMemory(ulong Address, ulong Size)
{
- long End = Position + Size;
+ ulong EndAddr = Address + Size;
lock (Blocks)
{
- long MappedSize = 0;
+ ulong MappedSize = 0;
KMemoryInfo Info;
- LinkedListNode<KMemoryBlock> BaseNode = FindBlockNode(Position);
-
- LinkedListNode<KMemoryBlock> Node = BaseNode;
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
do
{
@@ -688,57 +1333,66 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Info.State != MemoryState.Unmapped)
{
- MappedSize += GetSizeInRange(Info, Position, End);
+ MappedSize += GetSizeInRange(Info, Address, EndAddr);
}
Node = Node.Next;
}
- while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
+ while (Info.Address + Info.Size < EndAddr && Node != null);
if (MappedSize == Size)
{
- return 0;
+ return KernelResult.Success;
}
- long RemainingSize = Size - MappedSize;
+ ulong RemainingSize = Size - MappedSize;
+
+ ulong RemainingPages = RemainingSize / PageSize;
- if (!Allocator.TryAllocate(RemainingSize, out long PA))
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.ResourceLimit != null &&
+ !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Memory, RemainingSize))
{
- return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+ return KernelResult.ResLimitExceeded;
}
- Node = BaseNode;
+ KMemoryRegionManager Region = GetMemoryRegionManager();
- do
- {
- Info = Node.Value.GetInfo();
+ KernelResult Result = Region.AllocatePages(RemainingPages, AslrDisabled, out KPageList PageList);
- if (Info.State == MemoryState.Unmapped)
+ void CleanUpForError()
+ {
+ if (PageList != null)
{
- long CurrSize = GetSizeInRange(Info, Position, End);
+ Region.FreePages(PageList);
+ }
- long MapPosition = Info.Position;
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, RemainingSize);
+ }
- if ((ulong)MapPosition < (ulong)Position)
- {
- MapPosition = Position;
- }
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
- CpuMemory.Map(MapPosition, PA, CurrSize);
+ return Result;
+ }
- PA += CurrSize;
- }
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ CleanUpForError();
- Node = Node.Next;
+ return KernelResult.OutOfResource;
}
- while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
- PersonalMmHeapUsage += RemainingSize;
+ MapPhysicalMemory(PageList, Address, EndAddr);
- long PagesCount = Size / PageSize;
+ PhysicalMemoryUsage += RemainingSize;
+
+ ulong PagesCount = Size / PageSize;
InsertBlock(
- Position,
+ Address,
PagesCount,
MemoryState.Unmapped,
MemoryPermission.None,
@@ -748,22 +1402,26 @@ namespace Ryujinx.HLE.HOS.Kernel
MemoryAttribute.None);
}
- return 0;
+ return KernelResult.Success;
}
- public long UnmapPhysicalMemory(long Position, long Size)
+ public KernelResult UnmapPhysicalMemory(ulong Address, ulong Size)
{
- long End = Position + Size;
+ ulong EndAddr = Address + Size;
lock (Blocks)
{
- long HeapMappedSize = 0;
+ //Scan, ensure that the region can be unmapped (all blocks are heap or
+ //already unmapped), fill pages list for freeing memory.
+ ulong HeapMappedSize = 0;
- long CurrPosition = Position;
+ KPageList PageList = new KPageList();
KMemoryInfo Info;
- LinkedListNode<KMemoryBlock> Node = FindBlockNode(CurrPosition);
+ LinkedListNode<KMemoryBlock> BaseNode = FindBlockNode(Address);
+
+ LinkedListNode<KMemoryBlock> Node = BaseNode;
do
{
@@ -773,90 +1431,200 @@ namespace Ryujinx.HLE.HOS.Kernel
{
if (Info.Attribute != MemoryAttribute.None)
{
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ return KernelResult.InvalidMemState;
}
- HeapMappedSize += GetSizeInRange(Info, Position, End);
+ ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
+ ulong BlockAddress = GetAddrInRange(Info, Address);
+
+ AddVaRangeToPageList(PageList, BlockAddress, BlockSize / PageSize);
+
+ HeapMappedSize += BlockSize;
}
else if (Info.State != MemoryState.Unmapped)
{
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ return KernelResult.InvalidMemState;
}
Node = Node.Next;
}
- while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
+ while (Info.Address + Info.Size < EndAddr && Node != null);
if (HeapMappedSize == 0)
{
- return 0;
+ return KernelResult.Success;
+ }
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
}
- PersonalMmHeapUsage -= HeapMappedSize;
+ //Try to unmap all the heap mapped memory inside range.
+ KernelResult Result = KernelResult.Success;
- long PagesCount = Size / PageSize;
+ Node = BaseNode;
- InsertBlock(Position, PagesCount, MemoryState.Unmapped);
+ do
+ {
+ Info = Node.Value.GetInfo();
+
+ if (Info.State == MemoryState.Heap)
+ {
+ ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
+ ulong BlockAddress = GetAddrInRange(Info, Address);
- FreePages(Position, PagesCount);
+ ulong BlockPagesCount = BlockSize / PageSize;
- CpuMemory.Unmap(Position, Size);
+ Result = MmuUnmap(BlockAddress, BlockPagesCount);
- return 0;
+ if (Result != KernelResult.Success)
+ {
+ //If we failed to unmap, we need to remap everything back again.
+ MapPhysicalMemory(PageList, Address, BlockAddress + BlockSize);
+
+ break;
+ }
+ }
+
+ Node = Node.Next;
+ }
+ while (Info.Address + Info.Size < EndAddr && Node != null);
+
+ if (Result == KernelResult.Success)
+ {
+ GetMemoryRegionManager().FreePages(PageList);
+
+ PhysicalMemoryUsage -= HeapMappedSize;
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, HeapMappedSize);
+
+ ulong PagesCount = Size / PageSize;
+
+ InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+ }
+
+ return Result;
}
}
- private long GetSizeInRange(KMemoryInfo Info, long Start, long End)
+ private void MapPhysicalMemory(KPageList PageList, ulong Address, ulong EndAddr)
{
- long CurrEnd = Info.Size + Info.Position;
- long CurrSize = Info.Size;
+ KMemoryInfo Info;
- if ((ulong)Info.Position < (ulong)Start)
- {
- CurrSize -= Start - Info.Position;
- }
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
- if ((ulong)CurrEnd > (ulong)End)
+ LinkedListNode<KPageNode> PageListNode = PageList.Nodes.First;
+
+ KPageNode PageNode = PageListNode.Value;
+
+ ulong SrcPa = PageNode.Address;
+ ulong SrcPaPages = PageNode.PagesCount;
+
+ do
{
- CurrSize -= CurrEnd - End;
- }
+ Info = Node.Value.GetInfo();
+
+ if (Info.State == MemoryState.Unmapped)
+ {
+ ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
+
+ ulong DstVaPages = BlockSize / PageSize;
+
+ ulong DstVa = GetAddrInRange(Info, Address);
+
+ while (DstVaPages > 0)
+ {
+ if (SrcPaPages == 0)
+ {
+ PageListNode = PageListNode.Next;
+
+ PageNode = PageListNode.Value;
+
+ SrcPa = PageNode.Address;
+ SrcPaPages = PageNode.PagesCount;
+ }
+
+ ulong PagesCount = SrcPaPages;
+
+ if (PagesCount > DstVaPages)
+ {
+ PagesCount = DstVaPages;
+ }
+
+ DoMmuOperation(
+ DstVa,
+ PagesCount,
+ SrcPa,
+ true,
+ MemoryPermission.ReadAndWrite,
+ MemoryOperation.MapPa);
+
+ DstVa += PagesCount * PageSize;
+ SrcPa += PagesCount * PageSize;
+ SrcPaPages -= PagesCount;
+ DstVaPages -= PagesCount;
+ }
+ }
- return CurrSize;
+ Node = Node.Next;
+ }
+ while (Info.Address + Info.Size < EndAddr && Node != null);
}
- private void FreePages(long Position, long PagesCount)
+ private static ulong GetSizeInRange(KMemoryInfo Info, ulong Start, ulong End)
{
- for (long Page = 0; Page < PagesCount; Page++)
+ ulong EndAddr = Info.Size + Info.Address;
+ ulong Size = Info.Size;
+
+ if (Info.Address < Start)
{
- long VA = Position + Page * PageSize;
+ Size -= Start - Info.Address;
+ }
- if (!CpuMemory.IsMapped(VA))
- {
- continue;
- }
+ if (EndAddr > End)
+ {
+ Size -= EndAddr - End;
+ }
- long PA = CpuMemory.GetPhysicalAddress(VA);
+ return Size;
+ }
- Allocator.Free(PA, PageSize);
+ private static ulong GetAddrInRange(KMemoryInfo Info, ulong Start)
+ {
+ if (Info.Address < Start)
+ {
+ return Start;
}
+
+ return Info.Address;
}
- public bool HleIsUnmapped(long Position, long Size)
+ private void AddVaRangeToPageList(KPageList PageList, ulong Start, ulong PagesCount)
{
- bool Result = false;
+ ulong Address = Start;
- lock (Blocks)
+ while (Address < Start + PagesCount * PageSize)
{
- Result = IsUnmapped(Position, Size);
- }
+ KernelResult Result = ConvertVaToPa(Address, out ulong Pa);
- return Result;
+ if (Result != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure translating virtual address.");
+ }
+
+ PageList.AddRange(Pa, 1);
+
+ Address += PageSize;
+ }
}
- private bool IsUnmapped(long Position, long Size)
+ private bool IsUnmapped(ulong Address, ulong Size)
{
return CheckRange(
- Position,
+ Address,
Size,
MemoryState.Mask,
MemoryState.Unmapped,
@@ -871,8 +1639,8 @@ namespace Ryujinx.HLE.HOS.Kernel
}
private bool CheckRange(
- long Position,
- long Size,
+ ulong Address,
+ ulong Size,
MemoryState StateMask,
MemoryState StateExpected,
MemoryPermission PermissionMask,
@@ -884,24 +1652,44 @@ namespace Ryujinx.HLE.HOS.Kernel
out MemoryPermission OutPermission,
out MemoryAttribute OutAttribute)
{
- KMemoryInfo BlkInfo = FindBlock(Position).GetInfo();
+ ulong EndAddr = Address + Size - 1;
+
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
+
+ KMemoryInfo Info = Node.Value.GetInfo();
- ulong Start = (ulong)Position;
- ulong End = (ulong)Size + Start;
+ MemoryState FirstState = Info.State;
+ MemoryPermission FirstPermission = Info.Permission;
+ MemoryAttribute FirstAttribute = Info.Attribute;
- if (End <= (ulong)(BlkInfo.Position + BlkInfo.Size))
+ do
{
- if ((BlkInfo.Attribute & AttributeMask) == AttributeExpected &&
- (BlkInfo.State & StateMask) == StateExpected &&
- (BlkInfo.Permission & PermissionMask) == PermissionExpected)
+ Info = Node.Value.GetInfo();
+
+ //Check if the block state matches what we expect.
+ if ( FirstState != Info.State ||
+ FirstPermission != Info.Permission ||
+ (Info.Attribute & AttributeMask) != AttributeExpected ||
+ (FirstAttribute | AttributeIgnoreMask) != (Info.Attribute | AttributeIgnoreMask) ||
+ (FirstState & StateMask) != StateExpected ||
+ (FirstPermission & PermissionMask) != PermissionExpected)
+ {
+ break;
+ }
+
+ //Check if this is the last block on the range, if so return success.
+ if (EndAddr <= Info.Address + Info.Size - 1)
{
- OutState = BlkInfo.State;
- OutPermission = BlkInfo.Permission;
- OutAttribute = BlkInfo.Attribute & ~AttributeIgnoreMask;
+ OutState = FirstState;
+ OutPermission = FirstPermission;
+ OutAttribute = FirstAttribute & ~AttributeIgnoreMask;
return true;
}
+
+ Node = Node.Next;
}
+ while (Node != null);
OutState = MemoryState.Unmapped;
OutPermission = MemoryPermission.None;
@@ -910,9 +1698,48 @@ namespace Ryujinx.HLE.HOS.Kernel
return false;
}
+ private bool CheckRange(
+ ulong Address,
+ ulong Size,
+ MemoryState StateMask,
+ MemoryState StateExpected,
+ MemoryPermission PermissionMask,
+ MemoryPermission PermissionExpected,
+ MemoryAttribute AttributeMask,
+ MemoryAttribute AttributeExpected)
+ {
+ ulong EndAddr = Address + Size - 1;
+
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
+
+ do
+ {
+ KMemoryInfo Info = Node.Value.GetInfo();
+
+ //Check if the block state matches what we expect.
+ if ((Info.State & StateMask) != StateExpected ||
+ (Info.Permission & PermissionMask) != PermissionExpected ||
+ (Info.Attribute & AttributeMask) != AttributeExpected)
+ {
+ break;
+ }
+
+ //Check if this is the last block on the range, if so return success.
+ if (EndAddr <= Info.Address + Info.Size - 1)
+ {
+ return true;
+ }
+
+ Node = Node.Next;
+ }
+ while (Node != null);
+
+ return false;
+ }
+
private void InsertBlock(
- long BasePosition,
- long PagesCount,
+ ulong BaseAddress,
+ ulong PagesCount,
MemoryState OldState,
MemoryPermission OldPermission,
MemoryAttribute OldAttribute,
@@ -923,10 +1750,11 @@ namespace Ryujinx.HLE.HOS.Kernel
//Insert new block on the list only on areas where the state
//of the block matches the state specified on the Old* state
//arguments, otherwise leave it as is.
+ int OldCount = Blocks.Count;
+
OldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
- ulong Start = (ulong)BasePosition;
- ulong End = (ulong)PagesCount * PageSize + Start;
+ ulong EndAddr = PagesCount * PageSize + BaseAddress;
LinkedListNode<KMemoryBlock> Node = Blocks.First;
@@ -937,10 +1765,10 @@ namespace Ryujinx.HLE.HOS.Kernel
KMemoryBlock CurrBlock = Node.Value;
- ulong CurrStart = (ulong)CurrBlock.BasePosition;
- ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart;
+ ulong CurrBaseAddr = CurrBlock.BaseAddress;
+ ulong CurrEndAddr = CurrBlock.PagesCount * PageSize + CurrBaseAddr;
- if (Start < CurrEnd && CurrStart < End)
+ if (BaseAddress < CurrEndAddr && CurrBaseAddr < EndAddr)
{
MemoryAttribute CurrBlockAttr = CurrBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped;
@@ -953,36 +1781,36 @@ namespace Ryujinx.HLE.HOS.Kernel
continue;
}
- if (CurrStart >= Start && CurrEnd <= End)
+ if (CurrBaseAddr >= BaseAddress && CurrEndAddr <= EndAddr)
{
CurrBlock.State = NewState;
CurrBlock.Permission = NewPermission;
CurrBlock.Attribute &= ~MemoryAttribute.IpcAndDeviceMapped;
CurrBlock.Attribute |= NewAttribute;
}
- else if (CurrStart >= Start)
+ else if (CurrBaseAddr >= BaseAddress)
{
- CurrBlock.BasePosition = (long)End;
+ CurrBlock.BaseAddress = EndAddr;
- CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize);
+ CurrBlock.PagesCount = (CurrEndAddr - EndAddr) / PageSize;
- long NewPagesCount = (long)((End - CurrStart) / PageSize);
+ ulong NewPagesCount = (EndAddr - CurrBaseAddr) / PageSize;
NewNode = Blocks.AddBefore(Node, new KMemoryBlock(
- (long)CurrStart,
+ CurrBaseAddr,
NewPagesCount,
NewState,
NewPermission,
NewAttribute));
}
- else if (CurrEnd <= End)
+ else if (CurrEndAddr <= EndAddr)
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- long NewPagesCount = (long)((CurrEnd - Start) / PageSize);
+ ulong NewPagesCount = (CurrEndAddr - BaseAddress) / PageSize;
NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
- BasePosition,
+ BaseAddress,
NewPagesCount,
NewState,
NewPermission,
@@ -990,19 +1818,19 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- long NextPagesCount = (long)((CurrEnd - End) / PageSize);
+ ulong NextPagesCount = (CurrEndAddr - EndAddr) / PageSize;
NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
- BasePosition,
+ BaseAddress,
PagesCount,
NewState,
NewPermission,
NewAttribute));
Blocks.AddAfter(NewNode, new KMemoryBlock(
- (long)End,
+ EndAddr,
NextPagesCount,
CurrBlock.State,
CurrBlock.Permission,
@@ -1016,21 +1844,24 @@ namespace Ryujinx.HLE.HOS.Kernel
Node = NextNode;
}
+
+ BlockAllocator.Count += Blocks.Count - OldCount;
}
private void InsertBlock(
- long BasePosition,
- long PagesCount,
+ ulong BaseAddress,
+ ulong PagesCount,
MemoryState State,
MemoryPermission Permission = MemoryPermission.None,
MemoryAttribute Attribute = MemoryAttribute.None)
{
//Inserts new block at the list, replacing and spliting
//existing blocks as needed.
- KMemoryBlock Block = new KMemoryBlock(BasePosition, PagesCount, State, Permission, Attribute);
+ KMemoryBlock Block = new KMemoryBlock(BaseAddress, PagesCount, State, Permission, Attribute);
+
+ int OldCount = Blocks.Count;
- ulong Start = (ulong)BasePosition;
- ulong End = (ulong)PagesCount * PageSize + Start;
+ ulong EndAddr = PagesCount * PageSize + BaseAddress;
LinkedListNode<KMemoryBlock> NewNode = null;
@@ -1042,26 +1873,26 @@ namespace Ryujinx.HLE.HOS.Kernel
LinkedListNode<KMemoryBlock> NextNode = Node.Next;
- ulong CurrStart = (ulong)CurrBlock.BasePosition;
- ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart;
+ ulong CurrBaseAddr = CurrBlock.BaseAddress;
+ ulong CurrEndAddr = CurrBlock.PagesCount * PageSize + CurrBaseAddr;
- if (Start < CurrEnd && CurrStart < End)
+ if (BaseAddress < CurrEndAddr && CurrBaseAddr < EndAddr)
{
- if (Start >= CurrStart && End <= CurrEnd)
+ if (BaseAddress >= CurrBaseAddr && EndAddr <= CurrEndAddr)
{
Block.Attribute |= CurrBlock.Attribute & MemoryAttribute.IpcAndDeviceMapped;
}
- if (Start > CurrStart && End < CurrEnd)
+ if (BaseAddress > CurrBaseAddr && EndAddr < CurrEndAddr)
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- long NextPagesCount = (long)((CurrEnd - End) / PageSize);
+ ulong NextPagesCount = (CurrEndAddr - EndAddr) / PageSize;
NewNode = Blocks.AddAfter(Node, Block);
Blocks.AddAfter(NewNode, new KMemoryBlock(
- (long)End,
+ EndAddr,
NextPagesCount,
CurrBlock.State,
CurrBlock.Permission,
@@ -1069,20 +1900,20 @@ namespace Ryujinx.HLE.HOS.Kernel
break;
}
- else if (Start <= CurrStart && End < CurrEnd)
+ else if (BaseAddress <= CurrBaseAddr && EndAddr < CurrEndAddr)
{
- CurrBlock.BasePosition = (long)End;
+ CurrBlock.BaseAddress = EndAddr;
- CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize);
+ CurrBlock.PagesCount = (CurrEndAddr - EndAddr) / PageSize;
if (NewNode == null)
{
NewNode = Blocks.AddBefore(Node, Block);
}
}
- else if (Start > CurrStart && End >= CurrEnd)
+ else if (BaseAddress > CurrBaseAddr && EndAddr >= CurrEndAddr)
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
if (NewNode == null)
{
@@ -1109,14 +1940,15 @@ namespace Ryujinx.HLE.HOS.Kernel
}
MergeEqualStateNeighbours(NewNode);
+
+ BlockAllocator.Count += Blocks.Count - OldCount;
}
private void MergeEqualStateNeighbours(LinkedListNode<KMemoryBlock> Node)
{
KMemoryBlock Block = Node.Value;
- ulong Start = (ulong)Block.BasePosition;
- ulong End = (ulong)Block.PagesCount * PageSize + Start;
+ ulong EndAddr = Block.PagesCount * PageSize + Block.BaseAddress;
if (Node.Previous != null)
{
@@ -1126,9 +1958,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
Blocks.Remove(Node.Previous);
- Block.BasePosition = Previous.BasePosition;
-
- Start = (ulong)Block.BasePosition;
+ Block.BaseAddress = Previous.BaseAddress;
}
}
@@ -1140,31 +1970,84 @@ namespace Ryujinx.HLE.HOS.Kernel
{
Blocks.Remove(Node.Next);
- End = (ulong)(Next.BasePosition + Next.PagesCount * PageSize);
+ EndAddr = Next.BaseAddress + Next.PagesCount * PageSize;
}
}
- Block.PagesCount = (long)((End - Start) / PageSize);
+ Block.PagesCount = (EndAddr - Block.BaseAddress) / PageSize;
}
- private static bool BlockStateEquals(KMemoryBlock LHS, KMemoryBlock RHS)
+ private static bool BlockStateEquals(KMemoryBlock Lhs, KMemoryBlock Rhs)
{
- return LHS.State == RHS.State &&
- LHS.Permission == RHS.Permission &&
- LHS.Attribute == RHS.Attribute &&
- LHS.DeviceRefCount == RHS.DeviceRefCount &&
- LHS.IpcRefCount == RHS.IpcRefCount;
+ return Lhs.State == Rhs.State &&
+ Lhs.Permission == Rhs.Permission &&
+ Lhs.Attribute == Rhs.Attribute &&
+ Lhs.DeviceRefCount == Rhs.DeviceRefCount &&
+ Lhs.IpcRefCount == Rhs.IpcRefCount;
}
- private KMemoryBlock FindBlock(long Position)
+ private ulong FindFirstFit(
+ ulong RegionStart,
+ ulong RegionPagesCount,
+ ulong NeededPagesCount,
+ int Alignment,
+ ulong ReservedStart,
+ ulong ReservedPagesCount)
{
- return FindBlockNode(Position)?.Value;
+ ulong ReservedSize = ReservedPagesCount * PageSize;
+
+ ulong TotalNeededSize = ReservedSize + NeededPagesCount * PageSize;
+
+ ulong RegionEndAddr = RegionStart + RegionPagesCount * PageSize;
+
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(RegionStart);
+
+ KMemoryInfo Info = Node.Value.GetInfo();
+
+ while (RegionEndAddr >= Info.Address)
+ {
+ if (Info.State == MemoryState.Unmapped)
+ {
+ ulong CurrBaseAddr = Info.Address + ReservedSize;
+ ulong CurrEndAddr = Info.Address + Info.Size - 1;
+
+ ulong Address = BitUtils.AlignDown(CurrBaseAddr, Alignment) + ReservedStart;
+
+ if (CurrBaseAddr > Address)
+ {
+ Address += (ulong)Alignment;
+ }
+
+ ulong AllocationEndAddr = Address + TotalNeededSize - 1;
+
+ if (AllocationEndAddr <= RegionEndAddr &&
+ AllocationEndAddr <= CurrEndAddr &&
+ Address < AllocationEndAddr)
+ {
+ return Address;
+ }
+ }
+
+ Node = Node.Next;
+
+ if (Node == null)
+ {
+ break;
+ }
+
+ Info = Node.Value.GetInfo();
+ }
+
+ return 0;
}
- private LinkedListNode<KMemoryBlock> FindBlockNode(long Position)
+ private KMemoryBlock FindBlock(ulong Address)
{
- ulong Addr = (ulong)Position;
+ return FindBlockNode(Address)?.Value;
+ }
+ private LinkedListNode<KMemoryBlock> FindBlockNode(ulong Address)
+ {
lock (Blocks)
{
LinkedListNode<KMemoryBlock> Node = Blocks.First;
@@ -1173,10 +2056,9 @@ namespace Ryujinx.HLE.HOS.Kernel
{
KMemoryBlock Block = Node.Value;
- ulong Start = (ulong)Block.BasePosition;
- ulong End = (ulong)Block.PagesCount * PageSize + Start;
+ ulong CurrEndAddr = Block.PagesCount * PageSize + Block.BaseAddress;
- if (Start <= Addr && End - 1 >= Addr)
+ if (Block.BaseAddress <= Address && CurrEndAddr - 1 >= Address)
{
return Node;
}
@@ -1187,5 +2069,390 @@ namespace Ryujinx.HLE.HOS.Kernel
return null;
}
+
+ private bool ValidateRegionForState(ulong Address, ulong Size, MemoryState State)
+ {
+ ulong EndAddr = Address + Size;
+
+ ulong RegionBaseAddr = GetBaseAddrForState(State);
+
+ ulong RegionEndAddr = RegionBaseAddr + GetSizeForState(State);
+
+ bool InsideRegion()
+ {
+ return RegionBaseAddr <= Address &&
+ EndAddr > Address &&
+ EndAddr - 1 <= RegionEndAddr - 1;
+ }
+
+ bool OutsideHeapRegion()
+ {
+ return EndAddr <= HeapRegionStart ||
+ Address >= HeapRegionEnd;
+ }
+
+ bool OutsideMapRegion()
+ {
+ return EndAddr <= AliasRegionStart ||
+ Address >= AliasRegionEnd;
+ }
+
+ switch (State)
+ {
+ case MemoryState.Io:
+ case MemoryState.Normal:
+ case MemoryState.CodeStatic:
+ case MemoryState.CodeMutable:
+ case MemoryState.SharedMemory:
+ case MemoryState.ModCodeStatic:
+ case MemoryState.ModCodeMutable:
+ case MemoryState.Stack:
+ case MemoryState.ThreadLocal:
+ case MemoryState.TransferMemoryIsolated:
+ case MemoryState.TransferMemory:
+ case MemoryState.ProcessMemory:
+ case MemoryState.CodeReadOnly:
+ case MemoryState.CodeWritable:
+ return InsideRegion() && OutsideHeapRegion() && OutsideMapRegion();
+
+ case MemoryState.Heap:
+ return InsideRegion() && OutsideMapRegion();
+
+ case MemoryState.IpcBuffer0:
+ case MemoryState.IpcBuffer1:
+ case MemoryState.IpcBuffer3:
+ return InsideRegion() && OutsideHeapRegion();
+
+ case MemoryState.KernelStack:
+ return InsideRegion();
+ }
+
+ throw new ArgumentException($"Invalid state value \"{State}\".");
+ }
+
+ private ulong GetBaseAddrForState(MemoryState State)
+ {
+ switch (State)
+ {
+ case MemoryState.Io:
+ case MemoryState.Normal:
+ case MemoryState.ThreadLocal:
+ return TlsIoRegionStart;
+
+ case MemoryState.CodeStatic:
+ case MemoryState.CodeMutable:
+ case MemoryState.SharedMemory:
+ case MemoryState.ModCodeStatic:
+ case MemoryState.ModCodeMutable:
+ case MemoryState.TransferMemoryIsolated:
+ case MemoryState.TransferMemory:
+ case MemoryState.ProcessMemory:
+ case MemoryState.CodeReadOnly:
+ case MemoryState.CodeWritable:
+ return GetAddrSpaceBaseAddr();
+
+ case MemoryState.Heap:
+ return HeapRegionStart;
+
+ case MemoryState.IpcBuffer0:
+ case MemoryState.IpcBuffer1:
+ case MemoryState.IpcBuffer3:
+ return AliasRegionStart;
+
+ case MemoryState.Stack:
+ return StackRegionStart;
+
+ case MemoryState.KernelStack:
+ return AddrSpaceStart;
+ }
+
+ throw new ArgumentException($"Invalid state value \"{State}\".");
+ }
+
+ private ulong GetSizeForState(MemoryState State)
+ {
+ switch (State)
+ {
+ case MemoryState.Io:
+ case MemoryState.Normal:
+ case MemoryState.ThreadLocal:
+ return TlsIoRegionEnd - TlsIoRegionStart;
+
+ case MemoryState.CodeStatic:
+ case MemoryState.CodeMutable:
+ case MemoryState.SharedMemory:
+ case MemoryState.ModCodeStatic:
+ case MemoryState.ModCodeMutable:
+ case MemoryState.TransferMemoryIsolated:
+ case MemoryState.TransferMemory:
+ case MemoryState.ProcessMemory:
+ case MemoryState.CodeReadOnly:
+ case MemoryState.CodeWritable:
+ return GetAddrSpaceSize();
+
+ case MemoryState.Heap:
+ return HeapRegionEnd - HeapRegionStart;
+
+ case MemoryState.IpcBuffer0:
+ case MemoryState.IpcBuffer1:
+ case MemoryState.IpcBuffer3:
+ return AliasRegionEnd - AliasRegionStart;
+
+ case MemoryState.Stack:
+ return StackRegionEnd - StackRegionStart;
+
+ case MemoryState.KernelStack:
+ return AddrSpaceEnd - AddrSpaceStart;
+ }
+
+ throw new ArgumentException($"Invalid state value \"{State}\".");
+ }
+
+ public ulong GetAddrSpaceBaseAddr()
+ {
+ if (AddrSpaceWidth == 36 || AddrSpaceWidth == 39)
+ {
+ return 0x8000000;
+ }
+ else if (AddrSpaceWidth == 32)
+ {
+ return 0x200000;
+ }
+ else
+ {
+ throw new InvalidOperationException("Invalid address space width!");
+ }
+ }
+
+ public ulong GetAddrSpaceSize()
+ {
+ if (AddrSpaceWidth == 36)
+ {
+ return 0xff8000000;
+ }
+ else if (AddrSpaceWidth == 39)
+ {
+ return 0x7ff8000000;
+ }
+ else if (AddrSpaceWidth == 32)
+ {
+ return 0xffe00000;
+ }
+ else
+ {
+ throw new InvalidOperationException("Invalid address space width!");
+ }
+ }
+
+ private KernelResult MapPages(ulong Address, KPageList PageList, MemoryPermission Permission)
+ {
+ ulong CurrAddr = Address;
+
+ KernelResult Result = KernelResult.Success;
+
+ foreach (KPageNode PageNode in PageList)
+ {
+ Result = DoMmuOperation(
+ CurrAddr,
+ PageNode.PagesCount,
+ PageNode.Address,
+ true,
+ Permission,
+ MemoryOperation.MapPa);
+
+ if (Result != KernelResult.Success)
+ {
+ KMemoryInfo Info = FindBlock(CurrAddr).GetInfo();
+
+ ulong PagesCount = (Address - CurrAddr) / PageSize;
+
+ Result = MmuUnmap(Address, PagesCount);
+
+ break;
+ }
+
+ CurrAddr += PageNode.PagesCount * PageSize;
+ }
+
+ return Result;
+ }
+
+ private KernelResult MmuUnmap(ulong Address, ulong PagesCount)
+ {
+ return DoMmuOperation(
+ Address,
+ PagesCount,
+ 0,
+ false,
+ MemoryPermission.None,
+ MemoryOperation.Unmap);
+ }
+
+ private KernelResult MmuChangePermission(ulong Address, ulong PagesCount, MemoryPermission Permission)
+ {
+ return DoMmuOperation(
+ Address,
+ PagesCount,
+ 0,
+ false,
+ Permission,
+ MemoryOperation.ChangePermRw);
+ }
+
+ private KernelResult DoMmuOperation(
+ ulong DstVa,
+ ulong PagesCount,
+ ulong SrcPa,
+ bool Map,
+ MemoryPermission Permission,
+ MemoryOperation Operation)
+ {
+ if (Map != (Operation == MemoryOperation.MapPa))
+ {
+ throw new ArgumentException(nameof(Map) + " value is invalid for this operation.");
+ }
+
+ KernelResult Result;
+
+ switch (Operation)
+ {
+ case MemoryOperation.MapPa:
+ {
+ ulong Size = PagesCount * PageSize;
+
+ CpuMemory.Map((long)DstVa, (long)(SrcPa - DramMemoryMap.DramBase), (long)Size);
+
+ Result = KernelResult.Success;
+
+ break;
+ }
+
+ case MemoryOperation.Allocate:
+ {
+ KMemoryRegionManager Region = GetMemoryRegionManager();
+
+ Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList);
+
+ if (Result == KernelResult.Success)
+ {
+ Result = MmuMapPages(DstVa, PageList);
+ }
+
+ break;
+ }
+
+ case MemoryOperation.Unmap:
+ {
+ ulong Size = PagesCount * PageSize;
+
+ CpuMemory.Unmap((long)DstVa, (long)Size);
+
+ Result = KernelResult.Success;
+
+ break;
+ }
+
+ case MemoryOperation.ChangePermRw: Result = KernelResult.Success; break;
+ case MemoryOperation.ChangePermsAndAttributes: Result = KernelResult.Success; break;
+
+ default: throw new ArgumentException($"Invalid operation \"{Operation}\".");
+ }
+
+ return Result;
+ }
+
+ private KernelResult DoMmuOperation(
+ ulong Address,
+ ulong PagesCount,
+ KPageList PageList,
+ MemoryPermission Permission,
+ MemoryOperation Operation)
+ {
+ if (Operation != MemoryOperation.MapVa)
+ {
+ throw new ArgumentException($"Invalid memory operation \"{Operation}\" specified.");
+ }
+
+ return MmuMapPages(Address, PageList);
+ }
+
+ private KMemoryRegionManager GetMemoryRegionManager()
+ {
+ return System.MemoryRegions[(int)MemRegion];
+ }
+
+ private KernelResult MmuMapPages(ulong Address, KPageList PageList)
+ {
+ foreach (KPageNode PageNode in PageList)
+ {
+ ulong Size = PageNode.PagesCount * PageSize;
+
+ CpuMemory.Map((long)Address, (long)(PageNode.Address - DramMemoryMap.DramBase), (long)Size);
+
+ Address += Size;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult ConvertVaToPa(ulong Va, out ulong Pa)
+ {
+ Pa = DramMemoryMap.DramBase + (ulong)CpuMemory.GetPhysicalAddress((long)Va);
+
+ return KernelResult.Success;
+ }
+
+ public long GetMmUsedPages()
+ {
+ lock (Blocks)
+ {
+ return BitUtils.DivRoundUp(GetMmUsedSize(), PageSize);
+ }
+ }
+
+ private long GetMmUsedSize()
+ {
+ return Blocks.Count * KMemoryBlockSize;
+ }
+
+ public bool IsInvalidRegion(ulong Address, ulong Size)
+ {
+ return Address + Size - 1 > GetAddrSpaceBaseAddr() + GetAddrSpaceSize() - 1;
+ }
+
+ public bool InsideAddrSpace(ulong Address, ulong Size)
+ {
+ return AddrSpaceStart <= Address && Address + Size - 1 <= AddrSpaceEnd - 1;
+ }
+
+ public bool InsideAliasRegion(ulong Address, ulong Size)
+ {
+ return Address + Size > AliasRegionStart && AliasRegionEnd > Address;
+ }
+
+ public bool InsideHeapRegion(ulong Address, ulong Size)
+ {
+ return Address + Size > HeapRegionStart && HeapRegionEnd > Address;
+ }
+
+ public bool InsideStackRegion(ulong Address, ulong Size)
+ {
+ return Address + Size > StackRegionStart && StackRegionEnd > Address;
+ }
+
+ public bool OutsideAliasRegion(ulong Address, ulong Size)
+ {
+ return AliasRegionStart > Address || Address + Size - 1 > AliasRegionEnd - 1;
+ }
+
+ public bool OutsideAddrSpace(ulong Address, ulong Size)
+ {
+ return AddrSpaceStart > Address || Address + Size - 1 > AddrSpaceEnd - 1;
+ }
+
+ public bool OutsideStackRegion(ulong Address, ulong Size)
+ {
+ return StackRegionStart > Address || Address + Size - 1 > StackRegionEnd - 1;
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs
new file mode 100644
index 00000000..1f334e65
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs
@@ -0,0 +1,43 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KMemoryRegionBlock
+ {
+ public long[][] Masks;
+
+ public ulong FreeCount;
+ public int MaxLevel;
+ public ulong StartAligned;
+ public ulong SizeInBlocksTruncated;
+ public ulong SizeInBlocksRounded;
+ public int Order;
+ public int NextOrder;
+
+ public bool TryCoalesce(int Index, int Size)
+ {
+ long Mask = ((1L << Size) - 1) << (Index & 63);
+
+ Index /= 64;
+
+ if ((Mask & ~Masks[MaxLevel - 1][Index]) != 0)
+ {
+ return false;
+ }
+
+ Masks[MaxLevel - 1][Index] &= ~Mask;
+
+ for (int Level = MaxLevel - 2; Level >= 0; Level--, Index /= 64)
+ {
+ Masks[Level][Index / 64] &= ~(1L << (Index & 63));
+
+ if (Masks[Level][Index / 64] != 0)
+ {
+ break;
+ }
+ }
+
+ FreeCount -= (ulong)Size;
+
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs
new file mode 100644
index 00000000..10db0753
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs
@@ -0,0 +1,428 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KMemoryRegionManager
+ {
+ private static readonly int[] BlockOrders = new int[] { 12, 16, 21, 22, 25, 29, 30 };
+
+ public ulong Address { get; private set; }
+ public ulong EndAddr { get; private set; }
+ public ulong Size { get; private set; }
+
+ private int BlockOrdersCount;
+
+ private KMemoryRegionBlock[] Blocks;
+
+ public KMemoryRegionManager(ulong Address, ulong Size, ulong EndAddr)
+ {
+ Blocks = new KMemoryRegionBlock[BlockOrders.Length];
+
+ this.Address = Address;
+ this.Size = Size;
+ this.EndAddr = EndAddr;
+
+ BlockOrdersCount = BlockOrders.Length;
+
+ for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
+ {
+ Blocks[BlockIndex] = new KMemoryRegionBlock();
+
+ Blocks[BlockIndex].Order = BlockOrders[BlockIndex];
+
+ int NextOrder = BlockIndex == BlockOrdersCount - 1 ? 0 : BlockOrders[BlockIndex + 1];
+
+ Blocks[BlockIndex].NextOrder = NextOrder;
+
+ int CurrBlockSize = 1 << BlockOrders[BlockIndex];
+ int NextBlockSize = CurrBlockSize;
+
+ if (NextOrder != 0)
+ {
+ NextBlockSize = 1 << NextOrder;
+ }
+
+ ulong StartAligned = BitUtils.AlignDown(Address, NextBlockSize);
+ ulong EndAddrAligned = BitUtils.AlignDown(EndAddr, CurrBlockSize);
+
+ ulong SizeInBlocksTruncated = (EndAddrAligned - StartAligned) >> BlockOrders[BlockIndex];
+
+ ulong EndAddrRounded = BitUtils.AlignUp(Address + Size, NextBlockSize);
+
+ ulong SizeInBlocksRounded = (EndAddrRounded - StartAligned) >> BlockOrders[BlockIndex];
+
+ Blocks[BlockIndex].StartAligned = StartAligned;
+ Blocks[BlockIndex].SizeInBlocksTruncated = SizeInBlocksTruncated;
+ Blocks[BlockIndex].SizeInBlocksRounded = SizeInBlocksRounded;
+
+ ulong CurrSizeInBlocks = SizeInBlocksRounded;
+
+ int MaxLevel = 0;
+
+ do
+ {
+ MaxLevel++;
+ }
+ while ((CurrSizeInBlocks /= 64) != 0);
+
+ Blocks[BlockIndex].MaxLevel = MaxLevel;
+
+ Blocks[BlockIndex].Masks = new long[MaxLevel][];
+
+ CurrSizeInBlocks = SizeInBlocksRounded;
+
+ for (int Level = MaxLevel - 1; Level >= 0; Level--)
+ {
+ CurrSizeInBlocks = (CurrSizeInBlocks + 63) / 64;
+
+ Blocks[BlockIndex].Masks[Level] = new long[CurrSizeInBlocks];
+ }
+ }
+
+ if (Size != 0)
+ {
+ FreePages(Address, Size / KMemoryManager.PageSize);
+ }
+ }
+
+ public KernelResult AllocatePages(ulong PagesCount, bool Backwards, out KPageList PageList)
+ {
+ lock (Blocks)
+ {
+ return AllocatePagesImpl(PagesCount, Backwards, out PageList);
+ }
+ }
+
+ private KernelResult AllocatePagesImpl(ulong PagesCount, bool Backwards, out KPageList PageList)
+ {
+ PageList = new KPageList();
+
+ if (BlockOrdersCount > 0)
+ {
+ if (GetFreePagesImpl() < PagesCount)
+ {
+ return KernelResult.OutOfMemory;
+ }
+ }
+ else if (PagesCount != 0)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ for (int BlockIndex = BlockOrdersCount - 1; BlockIndex >= 0; BlockIndex--)
+ {
+ KMemoryRegionBlock Block = Blocks[BlockIndex];
+
+ ulong BestFitBlockSize = 1UL << Block.Order;
+
+ ulong BlockPagesCount = BestFitBlockSize / KMemoryManager.PageSize;
+
+ //Check if this is the best fit for this page size.
+ //If so, try allocating as much requested pages as possible.
+ while (BlockPagesCount <= PagesCount)
+ {
+ ulong Address = 0;
+
+ for (int CurrBlockIndex = BlockIndex;
+ CurrBlockIndex < BlockOrdersCount && Address == 0;
+ CurrBlockIndex++)
+ {
+ Block = Blocks[CurrBlockIndex];
+
+ int Index = 0;
+
+ bool ZeroMask = false;
+
+ for (int Level = 0; Level < Block.MaxLevel; Level++)
+ {
+ long Mask = Block.Masks[Level][Index];
+
+ if (Mask == 0)
+ {
+ ZeroMask = true;
+
+ break;
+ }
+
+ if (Backwards)
+ {
+ Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
+ }
+ else
+ {
+ Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
+ }
+ }
+
+ if (Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
+ {
+ continue;
+ }
+
+ Block.FreeCount--;
+
+ int TempIdx = Index;
+
+ for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
+ {
+ Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
+
+ if (Block.Masks[Level][TempIdx / 64] != 0)
+ {
+ break;
+ }
+ }
+
+ Address = Block.StartAligned + ((ulong)Index << Block.Order);
+ }
+
+ for (int CurrBlockIndex = BlockIndex;
+ CurrBlockIndex < BlockOrdersCount && Address == 0;
+ CurrBlockIndex++)
+ {
+ Block = Blocks[CurrBlockIndex];
+
+ int Index = 0;
+
+ bool ZeroMask = false;
+
+ for (int Level = 0; Level < Block.MaxLevel; Level++)
+ {
+ long Mask = Block.Masks[Level][Index];
+
+ if (Mask == 0)
+ {
+ ZeroMask = true;
+
+ break;
+ }
+
+ if (Backwards)
+ {
+ Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
+ }
+ else
+ {
+ Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
+ }
+ }
+
+ if (Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
+ {
+ continue;
+ }
+
+ Block.FreeCount--;
+
+ int TempIdx = Index;
+
+ for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
+ {
+ Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
+
+ if (Block.Masks[Level][TempIdx / 64] != 0)
+ {
+ break;
+ }
+ }
+
+ Address = Block.StartAligned + ((ulong)Index << Block.Order);
+ }
+
+ //The address being zero means that no free space was found on that order,
+ //just give up and try with the next one.
+ if (Address == 0)
+ {
+ break;
+ }
+
+ //If we are using a larger order than best fit, then we should
+ //split it into smaller blocks.
+ ulong FirstFreeBlockSize = 1UL << Block.Order;
+
+ if (FirstFreeBlockSize > BestFitBlockSize)
+ {
+ FreePages(Address + BestFitBlockSize, (FirstFreeBlockSize - BestFitBlockSize) / KMemoryManager.PageSize);
+ }
+
+ //Add new allocated page(s) to the pages list.
+ //If an error occurs, then free all allocated pages and fail.
+ KernelResult Result = PageList.AddRange(Address, BlockPagesCount);
+
+ if (Result != KernelResult.Success)
+ {
+ FreePages(Address, BlockPagesCount);
+
+ foreach (KPageNode PageNode in PageList)
+ {
+ FreePages(PageNode.Address, PageNode.PagesCount);
+ }
+
+ return Result;
+ }
+
+ PagesCount -= BlockPagesCount;
+ }
+ }
+
+ //Success case, all requested pages were allocated successfully.
+ if (PagesCount == 0)
+ {
+ return KernelResult.Success;
+ }
+
+ //Error case, free allocated pages and return out of memory.
+ foreach (KPageNode PageNode in PageList)
+ {
+ FreePages(PageNode.Address, PageNode.PagesCount);
+ }
+
+ PageList = null;
+
+ return KernelResult.OutOfMemory;
+ }
+
+ public void FreePages(KPageList PageList)
+ {
+ lock (Blocks)
+ {
+ foreach (KPageNode PageNode in PageList)
+ {
+ FreePages(PageNode.Address, PageNode.PagesCount);
+ }
+ }
+ }
+
+ private void FreePages(ulong Address, ulong PagesCount)
+ {
+ ulong EndAddr = Address + PagesCount * KMemoryManager.PageSize;
+
+ int BlockIndex = BlockOrdersCount - 1;
+
+ ulong AddressRounded = 0;
+ ulong EndAddrTruncated = 0;
+
+ for (; BlockIndex >= 0; BlockIndex--)
+ {
+ KMemoryRegionBlock AllocInfo = Blocks[BlockIndex];
+
+ int BlockSize = 1 << AllocInfo.Order;
+
+ AddressRounded = BitUtils.AlignUp (Address, BlockSize);
+ EndAddrTruncated = BitUtils.AlignDown(EndAddr, BlockSize);
+
+ if (AddressRounded < EndAddrTruncated)
+ {
+ break;
+ }
+ }
+
+ void FreeRegion(ulong CurrAddress)
+ {
+ for (int CurrBlockIndex = BlockIndex;
+ CurrBlockIndex < BlockOrdersCount && CurrAddress != 0;
+ CurrBlockIndex++)
+ {
+ KMemoryRegionBlock Block = Blocks[CurrBlockIndex];
+
+ Block.FreeCount++;
+
+ ulong FreedBlocks = (CurrAddress - Block.StartAligned) >> Block.Order;
+
+ int Index = (int)FreedBlocks;
+
+ for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, Index /= 64)
+ {
+ long Mask = Block.Masks[Level][Index / 64];
+
+ Block.Masks[Level][Index / 64] = Mask | (1L << (Index & 63));
+
+ if (Mask != 0)
+ {
+ break;
+ }
+ }
+
+ int BlockSizeDelta = 1 << (Block.NextOrder - Block.Order);
+
+ int FreedBlocksTruncated = BitUtils.AlignDown((int)FreedBlocks, BlockSizeDelta);
+
+ if (!Block.TryCoalesce(FreedBlocksTruncated, BlockSizeDelta))
+ {
+ break;
+ }
+
+ CurrAddress = Block.StartAligned + ((ulong)FreedBlocksTruncated << Block.Order);
+ }
+ }
+
+ //Free inside aligned region.
+ ulong BaseAddress = AddressRounded;
+
+ while (BaseAddress < EndAddrTruncated)
+ {
+ ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
+
+ FreeRegion(BaseAddress);
+
+ BaseAddress += BlockSize;
+ }
+
+ int NextBlockIndex = BlockIndex - 1;
+
+ //Free region between Address and aligned region start.
+ BaseAddress = AddressRounded;
+
+ for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
+ {
+ ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
+
+ while (BaseAddress - BlockSize >= Address)
+ {
+ BaseAddress -= BlockSize;
+
+ FreeRegion(BaseAddress);
+ }
+ }
+
+ //Free region between aligned region end and End Address.
+ BaseAddress = EndAddrTruncated;
+
+ for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
+ {
+ ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
+
+ while (BaseAddress + BlockSize <= EndAddr)
+ {
+ FreeRegion(BaseAddress);
+
+ BaseAddress += BlockSize;
+ }
+ }
+ }
+
+ public ulong GetFreePages()
+ {
+ lock (Blocks)
+ {
+ return GetFreePagesImpl();
+ }
+ }
+
+ private ulong GetFreePagesImpl()
+ {
+ ulong AvailablePages = 0;
+
+ for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
+ {
+ KMemoryRegionBlock Block = Blocks[BlockIndex];
+
+ ulong BlockPagesCount = (1UL << Block.Order) / KMemoryManager.PageSize;
+
+ AvailablePages += BlockPagesCount * Block.FreeCount;
+ }
+
+ return AvailablePages;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KPageList.cs b/Ryujinx.HLE/HOS/Kernel/KPageList.cs
new file mode 100644
index 00000000..05162323
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KPageList.cs
@@ -0,0 +1,80 @@
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KPageList : IEnumerable<KPageNode>
+ {
+ public LinkedList<KPageNode> Nodes { get; private set; }
+
+ public KPageList()
+ {
+ Nodes = new LinkedList<KPageNode>();
+ }
+
+ public KernelResult AddRange(ulong Address, ulong PagesCount)
+ {
+ if (PagesCount != 0)
+ {
+ if (Nodes.Last != null)
+ {
+ KPageNode LastNode = Nodes.Last.Value;
+
+ if (LastNode.Address + LastNode.PagesCount * KMemoryManager.PageSize == Address)
+ {
+ Address = LastNode.Address;
+ PagesCount += LastNode.PagesCount;
+
+ Nodes.RemoveLast();
+ }
+ }
+
+ Nodes.AddLast(new KPageNode(Address, PagesCount));
+ }
+
+ return KernelResult.Success;
+ }
+
+ public ulong GetPagesCount()
+ {
+ ulong Sum = 0;
+
+ foreach (KPageNode Node in Nodes)
+ {
+ Sum += Node.PagesCount;
+ }
+
+ return Sum;
+ }
+
+ public bool IsEqual(KPageList Other)
+ {
+ LinkedListNode<KPageNode> ThisNode = Nodes.First;
+ LinkedListNode<KPageNode> OtherNode = Other.Nodes.First;
+
+ while (ThisNode != null && OtherNode != null)
+ {
+ if (ThisNode.Value.Address != OtherNode.Value.Address ||
+ ThisNode.Value.PagesCount != OtherNode.Value.PagesCount)
+ {
+ return false;
+ }
+
+ ThisNode = ThisNode.Next;
+ OtherNode = OtherNode.Next;
+ }
+
+ return ThisNode == null && OtherNode == null;
+ }
+
+ public IEnumerator<KPageNode> GetEnumerator()
+ {
+ return Nodes.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KPageNode.cs b/Ryujinx.HLE/HOS/Kernel/KPageNode.cs
new file mode 100644
index 00000000..6cecab2e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KPageNode.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ struct KPageNode
+ {
+ public ulong Address;
+ public ulong PagesCount;
+
+ public KPageNode(ulong Address, ulong PagesCount)
+ {
+ this.Address = Address;
+ this.PagesCount = PagesCount;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KPort.cs b/Ryujinx.HLE/HOS/Kernel/KPort.cs
new file mode 100644
index 00000000..598f3a32
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KPort.cs
@@ -0,0 +1,26 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KPort : KAutoObject
+ {
+ public KServerPort ServerPort { get; private set; }
+ public KClientPort ClientPort { get; private set; }
+
+ private long NameAddress;
+ private bool IsLight;
+
+ public KPort(Horizon System) : 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);
+
+ this.IsLight = IsLight;
+ this.NameAddress = NameAddress;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/KProcess.cs
new file mode 100644
index 00000000..094ef222
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KProcess.cs
@@ -0,0 +1,1013 @@
+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; }
+
+ 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; }
+
+ private ProcessState State;
+
+ private object ProcessLock;
+ private object ThreadingLock;
+
+ public KAddressArbiter AddressArbiter { get; private set; }
+
+ public long[] RandomEntropy { get; private set; }
+
+ private bool Signaled;
+ private bool UseSystemMemBlocks;
+
+ public string Name { get; private set; }
+
+ private int ThreadCount;
+
+ public int MmuFlags { get; private set; }
+
+ private MemoryRegion MemRegion;
+
+ public KProcessCapabilities Capabilities { get; private set; }
+
+ public long TitleId { get; private set; }
+ public long Pid { get; private set; }
+
+ 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; }
+
+ private LinkedList<KThread> Threads;
+
+ public bool IsPaused { get; private set; }
+
+ public Translator Translator { get; private set; }
+
+ public MemoryManager CpuMemory { get; private set; }
+
+ private SvcHandler SvcHandler;
+
+ public HleProcessDebugger Debugger { get; private set; }
+
+ public KProcess(Horizon System) : base(System)
+ {
+ ProcessLock = new object();
+ ThreadingLock = new object();
+
+ CpuMemory = new MemoryManager(System.Device.Memory.RamPointer);
+
+ CpuMemory.InvalidAccess += InvalidAccessHandler;
+
+ AddressArbiter = new KAddressArbiter(System);
+
+ MemoryManager = new KMemoryManager(System, CpuMemory);
+
+ FullTlsPages = new SortedDictionary<ulong, KTlsPageInfo>();
+ FreeTlsPages = new SortedDictionary<ulong, KTlsPageInfo>();
+
+ Capabilities = new KProcessCapabilities();
+
+ RandomEntropy = new long[KScheduler.CpuCoresCount];
+
+ Threads = new LinkedList<KThread>();
+
+ Translator = new Translator();
+
+ Translator.CpuTrace += CpuTraceHandler;
+
+ SvcHandler = new SvcHandler(System.Device, this);
+
+ Debugger = new HleProcessDebugger(this);
+ }
+
+ public KernelResult InitializeKip(
+ ProcessCreationInfo CreationInfo,
+ int[] Caps,
+ KPageList PageList,
+ KResourceLimit ResourceLimit,
+ MemoryRegion MemRegion)
+ {
+ this.ResourceLimit = ResourceLimit;
+ this.MemRegion = MemRegion;
+
+ AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
+
+ bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0;
+
+ ulong CodeAddress = CreationInfo.CodeAddress;
+
+ ulong CodeSize = (ulong)CreationInfo.CodePagesCount * KMemoryManager.PageSize;
+
+ KMemoryBlockAllocator MemoryBlockAllocator = (MmuFlags & 0x40) != 0
+ ? System.LargeMemoryBlockAllocator
+ : System.SmallMemoryBlockAllocator;
+
+ KernelResult Result = MemoryManager.InitializeForProcess(
+ AddrSpaceType,
+ AslrEnabled,
+ !AslrEnabled,
+ MemRegion,
+ CodeAddress,
+ CodeSize,
+ MemoryBlockAllocator);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize))
+ {
+ return KernelResult.InvalidMemRange;
+ }
+
+ Result = MemoryManager.MapPages(
+ CodeAddress,
+ PageList,
+ MemoryState.CodeStatic,
+ MemoryPermission.None);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Result = Capabilities.InitializeForKernel(Caps, MemoryManager);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Pid = System.GetKipId();
+
+ if (Pid == 0 || (ulong)Pid >= Horizon.InitialProcessId)
+ {
+ throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
+ }
+
+ Result = ParseProcessInfo(CreationInfo);
+
+ return Result;
+ }
+
+ public KernelResult Initialize(
+ ProcessCreationInfo CreationInfo,
+ int[] Caps,
+ KResourceLimit ResourceLimit,
+ MemoryRegion MemRegion)
+ {
+ this.ResourceLimit = ResourceLimit;
+ this.MemRegion = MemRegion;
+
+ ulong PersonalMmHeapSize = GetPersonalMmHeapSize((ulong)CreationInfo.PersonalMmHeapPagesCount, MemRegion);
+
+ ulong CodePagesCount = (ulong)CreationInfo.CodePagesCount;
+
+ ulong NeededSizeForProcess = PersonalMmHeapSize + CodePagesCount * KMemoryManager.PageSize;
+
+ if (NeededSizeForProcess != 0 && ResourceLimit != null)
+ {
+ if (!ResourceLimit.Reserve(LimitableResource.Memory, NeededSizeForProcess))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+ }
+
+ void CleanUpForError()
+ {
+ if (NeededSizeForProcess != 0 && ResourceLimit != null)
+ {
+ ResourceLimit.Release(LimitableResource.Memory, NeededSizeForProcess);
+ }
+ }
+
+ PersonalMmHeapPagesCount = (ulong)CreationInfo.PersonalMmHeapPagesCount;
+
+ KMemoryBlockAllocator MemoryBlockAllocator;
+
+ if (PersonalMmHeapPagesCount != 0)
+ {
+ MemoryBlockAllocator = new KMemoryBlockAllocator(PersonalMmHeapPagesCount * KMemoryManager.PageSize);
+ }
+ else
+ {
+ MemoryBlockAllocator = (MmuFlags & 0x40) != 0
+ ? System.LargeMemoryBlockAllocator
+ : System.SmallMemoryBlockAllocator;
+ }
+
+ AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
+
+ bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0;
+
+ ulong CodeAddress = CreationInfo.CodeAddress;
+
+ ulong CodeSize = CodePagesCount * KMemoryManager.PageSize;
+
+ KernelResult Result = MemoryManager.InitializeForProcess(
+ AddrSpaceType,
+ AslrEnabled,
+ !AslrEnabled,
+ MemRegion,
+ CodeAddress,
+ CodeSize,
+ MemoryBlockAllocator);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize))
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidMemRange;
+ }
+
+ Result = MemoryManager.MapNewProcessCode(
+ CodeAddress,
+ CodePagesCount,
+ MemoryState.CodeStatic,
+ MemoryPermission.None);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ Result = Capabilities.InitializeForUser(Caps, MemoryManager);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ Pid = System.GetProcessId();
+
+ if (Pid == -1 || (ulong)Pid < Horizon.InitialProcessId)
+ {
+ throw new InvalidOperationException($"Invalid Process Id {Pid}.");
+ }
+
+ Result = ParseProcessInfo(CreationInfo);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+ }
+
+ return Result;
+ }
+
+ private bool ValidateCodeAddressAndSize(ulong Address, ulong Size)
+ {
+ ulong CodeRegionStart;
+ ulong CodeRegionSize;
+
+ switch (MemoryManager.AddrSpaceWidth)
+ {
+ case 32:
+ CodeRegionStart = 0x200000;
+ CodeRegionSize = 0x3fe00000;
+ break;
+
+ case 36:
+ CodeRegionStart = 0x8000000;
+ CodeRegionSize = 0x78000000;
+ break;
+
+ case 39:
+ CodeRegionStart = 0x8000000;
+ CodeRegionSize = 0x7ff8000000;
+ break;
+
+ default: throw new InvalidOperationException("Invalid address space width on memory manager.");
+ }
+
+ ulong EndAddr = Address + Size;
+
+ ulong CodeRegionEnd = CodeRegionStart + CodeRegionSize;
+
+ if (EndAddr <= Address ||
+ EndAddr - 1 > CodeRegionEnd - 1)
+ {
+ return false;
+ }
+
+ if (MemoryManager.InsideHeapRegion (Address, Size) ||
+ MemoryManager.InsideAliasRegion(Address, Size))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private KernelResult ParseProcessInfo(ProcessCreationInfo CreationInfo)
+ {
+ //Ensure that the current kernel version is equal or above to the minimum required.
+ uint RequiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19;
+ uint RequiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf;
+
+ if (System.EnableVersionChecks)
+ {
+ if (RequiredKernelVersionMajor > KernelVersionMajor)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ if (RequiredKernelVersionMajor != KernelVersionMajor && RequiredKernelVersionMajor < 3)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ if (RequiredKernelVersionMinor > KernelVersionMinor)
+ {
+ return KernelResult.InvalidCombination;
+ }
+ }
+
+ KernelResult Result = AllocateThreadLocalStorage(out ulong UserExceptionContextAddress);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ this.UserExceptionContextAddress = UserExceptionContextAddress;
+
+ MemoryHelper.FillWithZeros(CpuMemory, (long)UserExceptionContextAddress, KTlsPageInfo.TlsEntrySize);
+
+ Name = CreationInfo.Name;
+
+ State = ProcessState.Created;
+
+ CreationTimestamp = PerformanceCounter.ElapsedMilliseconds;
+
+ MmuFlags = CreationInfo.MmuFlags;
+ Category = CreationInfo.Category;
+ TitleId = CreationInfo.TitleId;
+ Entrypoint = CreationInfo.CodeAddress;
+ ImageSize = (ulong)CreationInfo.CodePagesCount * KMemoryManager.PageSize;
+
+ UseSystemMemBlocks = ((MmuFlags >> 6) & 1) != 0;
+
+ switch ((AddressSpaceType)((MmuFlags >> 1) & 7))
+ {
+ case AddressSpaceType.Addr32Bits:
+ case AddressSpaceType.Addr36Bits:
+ case AddressSpaceType.Addr39Bits:
+ MemoryUsageCapacity = MemoryManager.HeapRegionEnd -
+ MemoryManager.HeapRegionStart;
+ break;
+
+ case AddressSpaceType.Addr32BitsNoMap:
+ 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;
+ }
+
+ public KernelResult AllocateThreadLocalStorage(out ulong Address)
+ {
+ System.CriticalSection.Enter();
+
+ KernelResult Result;
+
+ if (FreeTlsPages.Count > 0)
+ {
+ //If we have free TLS pages available, just use the first one.
+ KTlsPageInfo PageInfo = FreeTlsPages.Values.First();
+
+ if (!PageInfo.TryGetFreePage(out Address))
+ {
+ throw new InvalidOperationException("Unexpected failure getting free TLS page!");
+ }
+
+ if (PageInfo.IsFull())
+ {
+ FreeTlsPages.Remove(PageInfo.PageAddr);
+
+ FullTlsPages.Add(PageInfo.PageAddr, PageInfo);
+ }
+
+ Result = KernelResult.Success;
+ }
+ else
+ {
+ //Otherwise, we need to create a new one.
+ Result = AllocateTlsPage(out KTlsPageInfo PageInfo);
+
+ if (Result == KernelResult.Success)
+ {
+ if (!PageInfo.TryGetFreePage(out Address))
+ {
+ throw new InvalidOperationException("Unexpected failure getting free TLS page!");
+ }
+
+ FreeTlsPages.Add(PageInfo.PageAddr, PageInfo);
+ }
+ else
+ {
+ Address = 0;
+ }
+ }
+
+ System.CriticalSection.Leave();
+
+ return Result;
+ }
+
+ private KernelResult AllocateTlsPage(out KTlsPageInfo PageInfo)
+ {
+ PageInfo = default(KTlsPageInfo);
+
+ if (!System.UserSlabHeapPages.TryGetItem(out ulong TlsPagePa))
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ ulong RegionStart = MemoryManager.TlsIoRegionStart;
+ ulong RegionSize = MemoryManager.TlsIoRegionEnd - RegionStart;
+
+ ulong RegionPagesCount = RegionSize / KMemoryManager.PageSize;
+
+ KernelResult Result = MemoryManager.AllocateOrMapPa(
+ 1,
+ KMemoryManager.PageSize,
+ TlsPagePa,
+ true,
+ RegionStart,
+ RegionPagesCount,
+ MemoryState.ThreadLocal,
+ MemoryPermission.ReadAndWrite,
+ out ulong TlsPageVa);
+
+ if (Result != KernelResult.Success)
+ {
+ System.UserSlabHeapPages.Free(TlsPagePa);
+ }
+ else
+ {
+ PageInfo = new KTlsPageInfo(TlsPageVa);
+
+ MemoryHelper.FillWithZeros(CpuMemory, (long)TlsPageVa, KMemoryManager.PageSize);
+ }
+
+ return Result;
+ }
+
+ public KernelResult FreeThreadLocalStorage(ulong TlsSlotAddr)
+ {
+ ulong TlsPageAddr = BitUtils.AlignDown(TlsSlotAddr, KMemoryManager.PageSize);
+
+ System.CriticalSection.Enter();
+
+ KernelResult Result = KernelResult.Success;
+
+ KTlsPageInfo PageInfo = null;
+
+ if (FullTlsPages.TryGetValue(TlsPageAddr, out PageInfo))
+ {
+ //TLS page was full, free slot and move to free pages tree.
+ FullTlsPages.Remove(TlsPageAddr);
+
+ FreeTlsPages.Add(TlsPageAddr, PageInfo);
+ }
+ else if (!FreeTlsPages.TryGetValue(TlsPageAddr, out PageInfo))
+ {
+ Result = KernelResult.InvalidAddress;
+ }
+
+ if (PageInfo != null)
+ {
+ PageInfo.FreeTlsSlot(TlsSlotAddr);
+
+ if (PageInfo.IsEmpty())
+ {
+ //TLS page is now empty, we should ensure it is removed
+ //from all trees, and free the memory it was using.
+ FreeTlsPages.Remove(TlsPageAddr);
+
+ System.CriticalSection.Leave();
+
+ FreeTlsPage(PageInfo);
+
+ return KernelResult.Success;
+ }
+ }
+
+ System.CriticalSection.Leave();
+
+ return Result;
+ }
+
+ private KernelResult FreeTlsPage(KTlsPageInfo PageInfo)
+ {
+ KernelResult Result = MemoryManager.ConvertVaToPa(PageInfo.PageAddr, out ulong TlsPagePa);
+
+ if (Result != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure translating virtual address to physical.");
+ }
+
+ Result = MemoryManager.UnmapForKernel(PageInfo.PageAddr, 1, MemoryState.ThreadLocal);
+
+ if (Result == KernelResult.Success)
+ {
+ System.UserSlabHeapPages.Free(TlsPagePa);
+ }
+
+ return Result;
+ }
+
+ private void GenerateRandomEntropy()
+ {
+ //TODO.
+ }
+
+ public KernelResult Start(int MainThreadPriority, ulong StackSize)
+ {
+ lock (ProcessLock)
+ {
+ if (State > ProcessState.CreatedAttached)
+ {
+ return KernelResult.InvalidState;
+ }
+
+ if (ResourceLimit != null && !ResourceLimit.Reserve(LimitableResource.Thread, 1))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+
+ KResourceLimit ThreadResourceLimit = ResourceLimit;
+ KResourceLimit MemoryResourceLimit = null;
+
+ if (MainThreadStackSize != 0)
+ {
+ throw new InvalidOperationException("Trying to start a process with a invalid state!");
+ }
+
+ ulong StackSizeRounded = BitUtils.AlignUp(StackSize, KMemoryManager.PageSize);
+
+ 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.
+ if (NeededSize > MemoryUsageCapacity ||
+ NeededSize < StackSizeRounded)
+ {
+ ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
+
+ return KernelResult.OutOfMemory;
+ }
+
+ if (StackSizeRounded != 0 && ResourceLimit != null)
+ {
+ MemoryResourceLimit = ResourceLimit;
+
+ if (!MemoryResourceLimit.Reserve(LimitableResource.Memory, StackSizeRounded))
+ {
+ ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
+
+ return KernelResult.ResLimitExceeded;
+ }
+ }
+
+ KernelResult Result;
+
+ KThread MainThread = null;
+
+ ulong StackTop = 0;
+
+ void CleanUpForError()
+ {
+ MainThread?.Terminate();
+ HandleTable.Destroy();
+
+ if (MainThreadStackSize != 0)
+ {
+ ulong StackBottom = StackTop - MainThreadStackSize;
+
+ ulong StackPagesCount = MainThreadStackSize / KMemoryManager.PageSize;
+
+ MemoryManager.UnmapForKernel(StackBottom, StackPagesCount, MemoryState.Stack);
+ }
+
+ MemoryResourceLimit?.Release(LimitableResource.Memory, StackSizeRounded);
+ ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
+ }
+
+ if (StackSizeRounded != 0)
+ {
+ ulong StackPagesCount = StackSizeRounded / KMemoryManager.PageSize;
+
+ ulong RegionStart = MemoryManager.StackRegionStart;
+ ulong RegionSize = MemoryManager.StackRegionEnd - RegionStart;
+
+ ulong RegionPagesCount = RegionSize / KMemoryManager.PageSize;
+
+ Result = MemoryManager.AllocateOrMapPa(
+ StackPagesCount,
+ KMemoryManager.PageSize,
+ 0,
+ false,
+ RegionStart,
+ RegionPagesCount,
+ MemoryState.Stack,
+ MemoryPermission.ReadAndWrite,
+ out ulong StackBottom);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ MainThreadStackSize += StackSizeRounded;
+
+ StackTop = StackBottom + StackSizeRounded;
+ }
+
+ ulong HeapCapacity = MemoryUsageCapacity - MainThreadStackSize - ImageSize;
+
+ Result = MemoryManager.SetHeapCapacity(HeapCapacity);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ HandleTable = new KHandleTable(System);
+
+ Result = HandleTable.Initialize(Capabilities.HandleTableSize);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ MainThread = new KThread(System);
+
+ Result = MainThread.Initialize(
+ Entrypoint,
+ 0,
+ StackTop,
+ MainThreadPriority,
+ DefaultCpuCore,
+ this);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ Result = HandleTable.GenerateHandle(MainThread, out int MainThreadHandle);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ MainThread.SetEntryArguments(0, MainThreadHandle);
+
+ ProcessState OldState = State;
+ ProcessState NewState = State != ProcessState.Created
+ ? ProcessState.Attached
+ : ProcessState.Started;
+
+ 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();
+ } */
+
+ MainThread.Reschedule(ThreadSchedState.Running);
+
+ return Result;
+ }
+ }
+
+ private void SetState(ProcessState NewState)
+ {
+ if (State != NewState)
+ {
+ State = NewState;
+ Signaled = true;
+
+ Signal();
+ }
+ }
+
+ public KernelResult InitializeThread(
+ KThread Thread,
+ ulong Entrypoint,
+ ulong ArgsPtr,
+ ulong StackTop,
+ int Priority,
+ int CpuCore)
+ {
+ lock (ProcessLock)
+ {
+ return Thread.Initialize(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, this);
+ }
+ }
+
+ public void SubscribeThreadEventHandlers(CpuThread Context)
+ {
+ Context.ThreadState.Interrupt += InterruptHandler;
+ Context.ThreadState.SvcCall += SvcHandler.SvcCall;
+ }
+
+ private void InterruptHandler(object sender, EventArgs e)
+ {
+ System.Scheduler.ContextSwitch();
+ }
+
+ public void IncrementThreadCount()
+ {
+ Interlocked.Increment(ref ThreadCount);
+
+ System.ThreadCounter.AddCount();
+ }
+
+ public void DecrementThreadCountAndTerminateIfZero()
+ {
+ System.ThreadCounter.Signal();
+
+ if (Interlocked.Decrement(ref ThreadCount) == 0)
+ {
+ Terminate();
+ }
+ }
+
+ public ulong GetMemoryCapacity()
+ {
+ ulong TotalCapacity = (ulong)ResourceLimit.GetRemainingValue(LimitableResource.Memory);
+
+ TotalCapacity += MemoryManager.GetTotalHeapSize();
+
+ TotalCapacity += GetPersonalMmHeapSize();
+
+ TotalCapacity += ImageSize + MainThreadStackSize;
+
+ if (TotalCapacity <= MemoryUsageCapacity)
+ {
+ return TotalCapacity;
+ }
+
+ return MemoryUsageCapacity;
+ }
+
+ public ulong GetMemoryUsage()
+ {
+ return ImageSize + MainThreadStackSize + MemoryManager.GetTotalHeapSize() + GetPersonalMmHeapSize();
+ }
+
+ public ulong GetMemoryCapacityWithoutPersonalMmHeap()
+ {
+ return GetMemoryCapacity() - GetPersonalMmHeapSize();
+ }
+
+ public ulong GetMemoryUsageWithoutPersonalMmHeap()
+ {
+ return GetMemoryUsage() - GetPersonalMmHeapSize();
+ }
+
+ private ulong GetPersonalMmHeapSize()
+ {
+ return GetPersonalMmHeapSize(PersonalMmHeapPagesCount, MemRegion);
+ }
+
+ private static ulong GetPersonalMmHeapSize(ulong PersonalMmHeapPagesCount, MemoryRegion MemRegion)
+ {
+ if (MemRegion == MemoryRegion.Applet)
+ {
+ return 0;
+ }
+
+ return PersonalMmHeapPagesCount * KMemoryManager.PageSize;
+ }
+
+ public void AddThread(KThread Thread)
+ {
+ lock (ThreadingLock)
+ {
+ Thread.ProcessListNode = Threads.AddLast(Thread);
+ }
+ }
+
+ public void RemoveThread(KThread Thread)
+ {
+ lock (ThreadingLock)
+ {
+ Threads.Remove(Thread.ProcessListNode);
+ }
+ }
+
+ public bool IsCpuCoreAllowed(int Core)
+ {
+ return (Capabilities.AllowedCpuCoresMask & (1L << Core)) != 0;
+ }
+
+ public bool IsPriorityAllowed(int Priority)
+ {
+ return (Capabilities.AllowedThreadPriosMask & (1L << Priority)) != 0;
+ }
+
+ public override bool IsSignaled()
+ {
+ return Signaled;
+ }
+
+ public KernelResult Terminate()
+ {
+ KernelResult Result;
+
+ bool ShallTerminate = false;
+
+ System.CriticalSection.Enter();
+
+ lock (ProcessLock)
+ {
+ if (State >= ProcessState.Started)
+ {
+ if (State == ProcessState.Started ||
+ State == ProcessState.Crashed ||
+ State == ProcessState.Attached ||
+ State == ProcessState.DebugSuspended)
+ {
+ SetState(ProcessState.Exiting);
+
+ ShallTerminate = true;
+ }
+
+ Result = KernelResult.Success;
+ }
+ else
+ {
+ Result = KernelResult.InvalidState;
+ }
+ }
+
+ System.CriticalSection.Leave();
+
+ if (ShallTerminate)
+ {
+ //UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
+
+ HandleTable.Destroy();
+
+ SignalExitForDebugEvent();
+ SignalExit();
+ }
+
+ return Result;
+ }
+
+ 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()
+ {
+ KernelResult Result;
+
+ System.CriticalSection.Enter();
+
+ lock (ProcessLock)
+ {
+ if (State != ProcessState.Exited && Signaled)
+ {
+ Signaled = false;
+
+ Result = KernelResult.Success;
+ }
+ else
+ {
+ Result = KernelResult.InvalidState;
+ }
+ }
+
+ System.CriticalSection.Leave();
+
+ return Result;
+ }
+
+ public void StopAllThreads()
+ {
+ lock (ThreadingLock)
+ {
+ foreach (KThread Thread in Threads)
+ {
+ Thread.Context.StopExecution();
+
+ 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}.");
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs
new file mode 100644
index 00000000..dfbe1f36
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs
@@ -0,0 +1,311 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KProcessCapabilities
+ {
+ public byte[] SvcAccessMask { get; private set; }
+ public byte[] IrqAccessMask { get; private set; }
+
+ public long AllowedCpuCoresMask { get; private set; }
+ public long AllowedThreadPriosMask { get; private set; }
+
+ public int DebuggingFlags { get; private set; }
+ public int HandleTableSize { get; private set; }
+ public int KernelReleaseVersion { get; private set; }
+ public int ApplicationType { get; private set; }
+
+ public KProcessCapabilities()
+ {
+ SvcAccessMask = new byte[0x10];
+ IrqAccessMask = new byte[0x80];
+ }
+
+ public KernelResult InitializeForKernel(int[] Caps, KMemoryManager MemoryManager)
+ {
+ AllowedCpuCoresMask = 0xf;
+ AllowedThreadPriosMask = -1;
+ DebuggingFlags &= ~3;
+ KernelReleaseVersion = KProcess.KernelVersionPacked;
+
+ return Parse(Caps, MemoryManager);
+ }
+
+ public KernelResult InitializeForUser(int[] Caps, KMemoryManager MemoryManager)
+ {
+ return Parse(Caps, MemoryManager);
+ }
+
+ private KernelResult Parse(int[] Caps, KMemoryManager MemoryManager)
+ {
+ int Mask0 = 0;
+ int Mask1 = 0;
+
+ for (int Index = 0; Index < Caps.Length; Index++)
+ {
+ int Cap = Caps[Index];
+
+ if (((Cap + 1) & ~Cap) != 0x40)
+ {
+ KernelResult Result = ParseCapability(Cap, ref Mask0, ref Mask1, MemoryManager);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+ }
+ else
+ {
+ if ((uint)Index + 1 >= Caps.Length)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ int PrevCap = Cap;
+
+ Cap = Caps[++Index];
+
+ if (((Cap + 1) & ~Cap) != 0x40)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ if ((Cap & 0x78000000) != 0)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ if ((Cap & 0x7ffff80) == 0)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ long Address = ((long)(uint)PrevCap << 5) & 0xffffff000;
+ long Size = ((long)(uint)Cap << 5) & 0xfffff000;
+
+ if (((ulong)(Address + Size - 1) >> 36) != 0)
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ MemoryPermission Perm = (PrevCap >> 31) != 0
+ ? MemoryPermission.Read
+ : MemoryPermission.ReadAndWrite;
+
+ KernelResult Result;
+
+ if ((Cap >> 31) != 0)
+ {
+ Result = MemoryManager.MapNormalMemory(Address, Size, Perm);
+ }
+ else
+ {
+ Result = MemoryManager.MapIoMemory(Address, Size, Perm);
+ }
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+ }
+ }
+
+ return KernelResult.Success;
+ }
+
+ private KernelResult ParseCapability(int Cap, ref int Mask0, ref int Mask1, KMemoryManager MemoryManager)
+ {
+ int Code = (Cap + 1) & ~Cap;
+
+ if (Code == 1)
+ {
+ return KernelResult.InvalidCapability;
+ }
+ else if (Code == 0)
+ {
+ return KernelResult.Success;
+ }
+
+ int CodeMask = 1 << (32 - BitUtils.CountLeadingZeros32(Code + 1));
+
+ //Check if the property was already set.
+ if (((Mask0 & CodeMask) & 0x1e008) != 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ Mask0 |= CodeMask;
+
+ switch (Code)
+ {
+ case 8:
+ {
+ if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
+ {
+ return KernelResult.InvalidCapability;
+ }
+
+ int LowestCpuCore = (Cap >> 16) & 0xff;
+ int HighestCpuCore = (Cap >> 24) & 0xff;
+
+ if (LowestCpuCore > HighestCpuCore)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ int HighestThreadPrio = (Cap >> 4) & 0x3f;
+ int LowestThreadPrio = (Cap >> 10) & 0x3f;
+
+ if (LowestThreadPrio > HighestThreadPrio)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ if (HighestCpuCore >= KScheduler.CpuCoresCount)
+ {
+ return KernelResult.InvalidCpuCore;
+ }
+
+ AllowedCpuCoresMask = GetMaskFromMinMax(LowestCpuCore, HighestCpuCore);
+ AllowedThreadPriosMask = GetMaskFromMinMax(LowestThreadPrio, HighestThreadPrio);
+
+ break;
+ }
+
+ case 0x10:
+ {
+ int Slot = (Cap >> 29) & 7;
+
+ int SvcSlotMask = 1 << Slot;
+
+ if ((Mask1 & SvcSlotMask) != 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ Mask1 |= SvcSlotMask;
+
+ int SvcMask = (Cap >> 5) & 0xffffff;
+
+ int BaseSvc = Slot * 24;
+
+ for (int Index = 0; Index < 24; Index++)
+ {
+ if (((SvcMask >> Index) & 1) == 0)
+ {
+ continue;
+ }
+
+ int SvcId = BaseSvc + Index;
+
+ if (SvcId > 0x7f)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ SvcAccessMask[SvcId / 8] |= (byte)(1 << (SvcId & 7));
+ }
+
+ break;
+ }
+
+ case 0x80:
+ {
+ long Address = ((long)(uint)Cap << 4) & 0xffffff000;
+
+ MemoryManager.MapIoMemory(Address, KMemoryManager.PageSize, MemoryPermission.ReadAndWrite);
+
+ break;
+ }
+
+ case 0x800:
+ {
+ //TODO: GIC distributor check.
+ int Irq0 = (Cap >> 12) & 0x3ff;
+ int Irq1 = (Cap >> 22) & 0x3ff;
+
+ if (Irq0 != 0x3ff)
+ {
+ IrqAccessMask[Irq0 / 8] |= (byte)(1 << (Irq0 & 7));
+ }
+
+ if (Irq1 != 0x3ff)
+ {
+ IrqAccessMask[Irq1 / 8] |= (byte)(1 << (Irq1 & 7));
+ }
+
+ break;
+ }
+
+ case 0x2000:
+ {
+ int ApplicationType = Cap >> 14;
+
+ if ((uint)ApplicationType > 7)
+ {
+ return KernelResult.ReservedValue;
+ }
+
+ this.ApplicationType = ApplicationType;
+
+ break;
+ }
+
+ case 0x4000:
+ {
+ //Note: This check is bugged on kernel too, we are just replicating the bug here.
+ if ((KernelReleaseVersion >> 17) != 0 || Cap < 0x80000)
+ {
+ return KernelResult.ReservedValue;
+ }
+
+ KernelReleaseVersion = Cap;
+
+ break;
+ }
+
+ case 0x8000:
+ {
+ int HandleTableSize = Cap >> 26;
+
+ if ((uint)HandleTableSize > 0x3ff)
+ {
+ return KernelResult.ReservedValue;
+ }
+
+ this.HandleTableSize = HandleTableSize;
+
+ break;
+ }
+
+ case 0x10000:
+ {
+ int DebuggingFlags = Cap >> 19;
+
+ if ((uint)DebuggingFlags > 3)
+ {
+ return KernelResult.ReservedValue;
+ }
+
+ this.DebuggingFlags &= ~3;
+ this.DebuggingFlags |= DebuggingFlags;
+
+ break;
+ }
+
+ default: return KernelResult.InvalidCapability;
+ }
+
+ return KernelResult.Success;
+ }
+
+ private static long GetMaskFromMinMax(int Min, int Max)
+ {
+ int Range = Max - Min + 1;
+
+ long Mask = (1L << Range) - 1;
+
+ return Mask << Min;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs
index d43fe824..bfb8e7e2 100644
--- a/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel
public override void Signal()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (!Signaled)
{
@@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Kernel
base.Signal();
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public KernelResult Clear()
@@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
KernelResult Result;
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (Signaled)
{
@@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Kernel
Result = KernelResult.InvalidState;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
diff --git a/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs b/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs
new file mode 100644
index 00000000..6fd70d0c
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs
@@ -0,0 +1,146 @@
+using Ryujinx.Common;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KResourceLimit
+ {
+ private const int Time10SecondsMs = 10000;
+
+ private long[] Current;
+ private long[] Limit;
+ private long[] Available;
+
+ private object LockObj;
+
+ private LinkedList<KThread> WaitingThreads;
+
+ private int WaitingThreadsCount;
+
+ private Horizon System;
+
+ public KResourceLimit(Horizon System)
+ {
+ Current = new long[(int)LimitableResource.Count];
+ Limit = new long[(int)LimitableResource.Count];
+ Available = new long[(int)LimitableResource.Count];
+
+ LockObj = new object();
+
+ WaitingThreads = new LinkedList<KThread>();
+
+ this.System = System;
+ }
+
+ public bool Reserve(LimitableResource Resource, ulong Amount)
+ {
+ return Reserve(Resource, (long)Amount);
+ }
+
+ public bool Reserve(LimitableResource Resource, long Amount)
+ {
+ return Reserve(Resource, Amount, KTimeManager.ConvertMillisecondsToNanoseconds(Time10SecondsMs));
+ }
+
+ public bool Reserve(LimitableResource Resource, long Amount, long Timeout)
+ {
+ long EndTimePoint = KTimeManager.ConvertNanosecondsToMilliseconds(Timeout);
+
+ EndTimePoint += PerformanceCounter.ElapsedMilliseconds;
+
+ bool Success = false;
+
+ int Index = GetIndex(Resource);
+
+ lock (LockObj)
+ {
+ long NewCurrent = Current[Index] + Amount;
+
+ while (NewCurrent > Limit[Index] && Available[Index] + Amount <= Limit[Index])
+ {
+ WaitingThreadsCount++;
+
+ KConditionVariable.Wait(System, WaitingThreads, LockObj, Timeout);
+
+ WaitingThreadsCount--;
+
+ NewCurrent = Current[Index] + Amount;
+
+ if (Timeout >= 0 && PerformanceCounter.ElapsedMilliseconds > EndTimePoint)
+ {
+ break;
+ }
+ }
+
+ if (NewCurrent <= Limit[Index])
+ {
+ Current[Index] = NewCurrent;
+
+ Success = true;
+ }
+ }
+
+ return Success;
+ }
+
+ public void Release(LimitableResource Resource, ulong Amount)
+ {
+ Release(Resource, (long)Amount);
+ }
+
+ public void Release(LimitableResource Resource, long Amount)
+ {
+ Release(Resource, Amount, Amount);
+ }
+
+ private void Release(LimitableResource Resource, long UsedAmount, long AvailableAmount)
+ {
+ int Index = GetIndex(Resource);
+
+ lock (LockObj)
+ {
+ Current [Index] -= UsedAmount;
+ Available[Index] -= AvailableAmount;
+
+ if (WaitingThreadsCount > 0)
+ {
+ KConditionVariable.NotifyAll(System, WaitingThreads);
+ }
+ }
+ }
+
+ public long GetRemainingValue(LimitableResource Resource)
+ {
+ int Index = GetIndex(Resource);
+
+ lock (LockObj)
+ {
+ return Limit[Index] - Current[Index];
+ }
+ }
+
+ public KernelResult SetLimitValue(LimitableResource Resource, long Limit)
+ {
+ int Index = GetIndex(Resource);
+
+ lock (LockObj)
+ {
+ if (Current[Index] <= Limit)
+ {
+ this.Limit[Index] = Limit;
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidState;
+ }
+ }
+ }
+
+ private static int GetIndex(LimitableResource Resource)
+ {
+ return (int)Resource;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs
index 3cfda419..3342f4a6 100644
--- a/Ryujinx.HLE/HOS/Kernel/KScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs
@@ -38,14 +38,14 @@ namespace Ryujinx.HLE.HOS.Kernel
private void PreemptThreads()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
PreemptThread(PreemptionPriorityCores012, 0);
PreemptThread(PreemptionPriorityCores012, 1);
PreemptThread(PreemptionPriorityCores012, 2);
PreemptThread(PreemptionPriorityCore3, 3);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
private void PreemptThread(int Prio, int Core)
@@ -82,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
//If the candidate was scheduled after the current thread, then it's not worth it.
- if (SelectedThread == null || SelectedThread.LastScheduledTicks >= Thread.LastScheduledTicks)
+ if (SelectedThread == null || SelectedThread.LastScheduledTime >= Thread.LastScheduledTime)
{
yield return Thread;
}
@@ -212,6 +212,11 @@ namespace Ryujinx.HLE.HOS.Kernel
throw new InvalidOperationException("Current thread is not scheduled!");
}
+ public KProcess GetCurrentProcess()
+ {
+ return GetCurrentThread().Owner;
+ }
+
public void Dispose()
{
Dispose(true);
diff --git a/Ryujinx.HLE/HOS/Kernel/KServerPort.cs b/Ryujinx.HLE/HOS/Kernel/KServerPort.cs
new file mode 100644
index 00000000..42135cd8
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KServerPort.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KServerPort : KSynchronizationObject
+ {
+ private KPort Parent;
+
+ public KServerPort(Horizon System) : base(System) { }
+
+ public void Initialize(KPort Parent)
+ {
+ this.Parent = Parent;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs
index cdd31667..a440438b 100644
--- a/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs
@@ -1,14 +1,68 @@
+using Ryujinx.Common;
+
namespace Ryujinx.HLE.HOS.Kernel
{
class KSharedMemory
{
- public long PA { get; private set; }
- public long Size { get; private set; }
+ private KPageList PageList;
+
+ private long OwnerPid;
+
+ private MemoryPermission OwnerPermission;
+ private MemoryPermission UserPermission;
+
+ public KSharedMemory(
+ KPageList PageList,
+ long OwnerPid,
+ MemoryPermission OwnerPermission,
+ MemoryPermission UserPermission)
+ {
+ this.PageList = PageList;
+ this.OwnerPid = OwnerPid;
+ this.OwnerPermission = OwnerPermission;
+ this.UserPermission = UserPermission;
+ }
- public KSharedMemory(long PA, long Size)
+ public KernelResult MapIntoProcess(
+ KMemoryManager MemoryManager,
+ ulong Address,
+ ulong Size,
+ KProcess Process,
+ MemoryPermission Permission)
{
- this.PA = PA;
- this.Size = Size;
+ ulong PagesCountRounded = BitUtils.DivRoundUp(Size, KMemoryManager.PageSize);
+
+ if (PageList.GetPagesCount() != PagesCountRounded)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ MemoryPermission ExpectedPermission = Process.Pid == OwnerPid
+ ? OwnerPermission
+ : UserPermission;
+
+ if (Permission != ExpectedPermission)
+ {
+ return KernelResult.InvalidPermission;
+ }
+
+ return MemoryManager.MapPages(Address, PageList, MemoryState.SharedMemory, Permission);
+ }
+
+ public KernelResult UnmapFromProcess(
+ KMemoryManager MemoryManager,
+ ulong Address,
+ ulong Size,
+ KProcess Process)
+ {
+ ulong PagesCountRounded = BitUtils.DivRoundUp(Size, KMemoryManager.PageSize);
+
+ if (PageList.GetPagesCount() != PagesCountRounded)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ return MemoryManager.UnmapPages(Address, PageList, MemoryState.SharedMemory);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs b/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs
new file mode 100644
index 00000000..2d6b3ca0
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KSlabHeap
+ {
+ private LinkedList<ulong> Items;
+
+ public KSlabHeap(ulong Pa, ulong ItemSize, ulong Size)
+ {
+ Items = new LinkedList<ulong>();
+
+ int ItemsCount = (int)(Size / ItemSize);
+
+ for (int Index = 0; Index < ItemsCount; Index++)
+ {
+ Items.AddLast(Pa);
+
+ Pa += ItemSize;
+ }
+ }
+
+ public bool TryGetItem(out ulong Pa)
+ {
+ lock (Items)
+ {
+ if (Items.First != null)
+ {
+ Pa = Items.First.Value;
+
+ Items.RemoveFirst();
+
+ return true;
+ }
+ }
+
+ Pa = 0;
+
+ return false;
+ }
+
+ public void Free(ulong Pa)
+ {
+ lock (Items)
+ {
+ Items.AddFirst(Pa);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs
index 57a6296c..19e700f4 100644
--- a/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs
@@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
long Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
//Check if objects are already signaled before waiting.
for (int Index = 0; Index < SyncObjs.Length; Index++)
@@ -29,14 +29,14 @@ namespace Ryujinx.HLE.HOS.Kernel
HndIndex = Index;
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return 0;
}
if (Timeout == 0)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
@@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Kernel
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
CurrentThread.WaitingSync = false;
@@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Kernel
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
}
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
Result = (uint)CurrentThread.ObjSyncResult;
@@ -100,14 +100,14 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
public void SignalObject(KSynchronizationObject SyncObj)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (SyncObj.IsSignaled())
{
@@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
KThread Thread = Node.Value;
- if ((Thread.SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused)
+ if ((Thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
{
Thread.SignaledObj = SyncObj;
Thread.ObjSyncResult = 0;
@@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs b/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs
index 28eac330..5ba7784f 100644
--- a/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs
@@ -2,16 +2,12 @@ using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel
{
- class KSynchronizationObject
+ class KSynchronizationObject : KAutoObject
{
public LinkedList<KThread> WaitingThreads;
- protected Horizon System;
-
- public KSynchronizationObject(Horizon System)
+ public KSynchronizationObject(Horizon System) : base(System)
{
- this.System = System;
-
WaitingThreads = new LinkedList<KThread>();
}
diff --git a/Ryujinx.HLE/HOS/Kernel/KThread.cs b/Ryujinx.HLE/HOS/Kernel/KThread.cs
index 73ee2322..88f144c8 100644
--- a/Ryujinx.HLE/HOS/Kernel/KThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KThread.cs
@@ -1,4 +1,5 @@
using ChocolArm64;
+using ChocolArm64.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -13,20 +14,30 @@ namespace Ryujinx.HLE.HOS.Kernel
public long AffinityMask { get; set; }
- public int ThreadId { get; private set; }
+ public long ThreadUid { get; private set; }
- public KSynchronizationObject SignaledObj;
+ public long TotalTimeRunning { get; set; }
+
+ public KSynchronizationObject SignaledObj { get; set; }
public long CondVarAddress { get; set; }
- public long MutexAddress { get; set; }
- public Process Owner { get; private set; }
+ private ulong Entrypoint;
+
+ public long MutexAddress { get; set; }
+
+ public KProcess Owner { get; private set; }
- public long LastScheduledTicks { get; set; }
+ private ulong TlsAddress;
+
+ public long LastScheduledTime { get; set; }
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
- private LinkedListNode<KThread> WithholderNode;
+ public LinkedList<KThread> Withholder { get; set; }
+ public LinkedListNode<KThread> WithholderNode { get; set; }
+
+ public LinkedListNode<KThread> ProcessListNode { get; set; }
private LinkedList<KThread> MutexWaiters;
private LinkedListNode<KThread> MutexWaiterNode;
@@ -65,38 +76,131 @@ namespace Ryujinx.HLE.HOS.Kernel
public long LastPc { get; set; }
- public KThread(
- CpuThread Thread,
- Process Process,
- Horizon System,
- int ProcessorId,
- int Priority,
- int ThreadId) : base(System)
+ public KThread(Horizon System) : base(System)
{
- this.ThreadId = ThreadId;
-
- Context = Thread;
- Owner = Process;
- PreferredCore = ProcessorId;
Scheduler = System.Scheduler;
SchedulingData = System.Scheduler.SchedulingData;
SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
MutexWaiters = new LinkedList<KThread>();
+ }
+
+ public KernelResult Initialize(
+ ulong Entrypoint,
+ ulong ArgsPtr,
+ ulong StackTop,
+ int Priority,
+ int DefaultCpuCore,
+ KProcess Owner,
+ ThreadType Type = ThreadType.User)
+ {
+ if ((uint)Type > 3)
+ {
+ throw new ArgumentException($"Invalid thread type \"{Type}\".");
+ }
- AffinityMask = 1 << ProcessorId;
+ PreferredCore = DefaultCpuCore;
- DynamicPriority = BasePriority = Priority;
+ AffinityMask |= 1L << DefaultCpuCore;
+
+ SchedFlags = Type == ThreadType.Dummy
+ ? ThreadSchedState.Running
+ : ThreadSchedState.None;
CurrentCore = PreferredCore;
+
+ DynamicPriority = Priority;
+ BasePriority = Priority;
+
+ ObjSyncResult = 0x7201;
+
+ this.Entrypoint = Entrypoint;
+
+ if (Type == ThreadType.User)
+ {
+ if (Owner.AllocateThreadLocalStorage(out TlsAddress) != KernelResult.Success)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ MemoryHelper.FillWithZeros(Owner.CpuMemory, (long)TlsAddress, KTlsPageInfo.TlsEntrySize);
+ }
+
+ bool Is64Bits;
+
+ if (Owner != null)
+ {
+ this.Owner = Owner;
+
+ Owner.IncrementThreadCount();
+
+ Is64Bits = (Owner.MmuFlags & 1) != 0;
+ }
+ else
+ {
+ Is64Bits = true;
+ }
+
+ Context = new CpuThread(Owner.Translator, Owner.CpuMemory, (long)Entrypoint);
+
+ Context.ThreadState.X0 = ArgsPtr;
+ Context.ThreadState.X31 = StackTop;
+
+ Context.ThreadState.CntfrqEl0 = 19200000;
+ Context.ThreadState.Tpidr = (long)TlsAddress;
+
+ Owner.SubscribeThreadEventHandlers(Context);
+
+ Context.WorkFinished += ThreadFinishedHandler;
+
+ ThreadUid = System.GetThreadUid();
+
+ if (Owner != null)
+ {
+ Owner.AddThread(this);
+
+ if (Owner.IsPaused)
+ {
+ System.CriticalSection.Enter();
+
+ if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
+ {
+ System.CriticalSection.Leave();
+
+ return KernelResult.Success;
+ }
+
+ ForcePauseFlags |= ThreadSchedState.ProcessPauseFlag;
+
+ CombineForcePauseFlags();
+
+ System.CriticalSection.Leave();
+ }
+ }
+
+ return KernelResult.Success;
}
- public long Start()
+ public KernelResult Start()
{
- long Result = MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
+ if (!System.KernelInitialized)
+ {
+ System.CriticalSection.Enter();
+
+ if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
+ {
+ ForcePauseFlags |= ThreadSchedState.KernelInitPauseFlag;
+
+ CombineForcePauseFlags();
+ }
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Leave();
+ }
+
+ KernelResult Result = KernelResult.ThreadTerminating;
+
+ System.CriticalSection.Enter();
if (!ShallBeTerminated)
{
@@ -106,9 +210,9 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.SchedFlags != ThreadSchedState.TerminationPending &&
!CurrentThread.ShallBeTerminated)
{
- if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.None)
+ if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None)
{
- Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
+ Result = KernelResult.InvalidState;
break;
}
@@ -122,7 +226,7 @@ namespace Ryujinx.HLE.HOS.Kernel
SetNewSchedFlags(ThreadSchedState.Running);
- Result = 0;
+ Result = KernelResult.Success;
break;
}
@@ -130,8 +234,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
CurrentThread.CombineForcePauseFlags();
- System.CriticalSectionLock.Unlock();
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Leave();
+ System.CriticalSection.Enter();
if (CurrentThread.ShallBeTerminated)
{
@@ -141,25 +245,25 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
public void Exit()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- ForcePauseFlags &= ~ThreadSchedState.ExceptionalMask;
+ ForcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
ExitImpl();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
private void ExitImpl()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
SetNewSchedFlags(ThreadSchedState.TerminationPending);
@@ -167,16 +271,16 @@ namespace Ryujinx.HLE.HOS.Kernel
Signal();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public long Sleep(long Timeout)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
}
@@ -188,7 +292,7 @@ namespace Ryujinx.HLE.HOS.Kernel
System.TimeManager.ScheduleFutureInvocation(this, Timeout);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
if (Timeout > 0)
{
@@ -200,11 +304,11 @@ namespace Ryujinx.HLE.HOS.Kernel
public void Yield()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
@@ -219,27 +323,27 @@ namespace Ryujinx.HLE.HOS.Kernel
Scheduler.ThreadReselectionRequested = true;
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
}
public void YieldWithLoadBalancing()
{
- System.CriticalSectionLock.Lock();
-
- int Prio = DynamicPriority;
- int Core = CurrentCore;
+ System.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
return;
}
+ int Prio = DynamicPriority;
+ int Core = CurrentCore;
+
KThread NextThreadOnCurrentQueue = null;
if (DynamicPriority < KScheduler.PrioritiesCount)
@@ -270,7 +374,7 @@ namespace Ryujinx.HLE.HOS.Kernel
//If the candidate was scheduled after the current thread, then it's not worth it,
//unless the priority is higher than the current one.
- if (NextThreadOnCurrentQueue.LastScheduledTicks >= Thread.LastScheduledTicks ||
+ if (NextThreadOnCurrentQueue.LastScheduledTime >= Thread.LastScheduledTime ||
NextThreadOnCurrentQueue.DynamicPriority < Thread.DynamicPriority)
{
yield return Thread;
@@ -292,18 +396,18 @@ namespace Ryujinx.HLE.HOS.Kernel
Scheduler.ThreadReselectionRequested = true;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
}
public void YieldAndWaitForLoadBalancing()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
@@ -348,47 +452,47 @@ namespace Ryujinx.HLE.HOS.Kernel
Scheduler.ThreadReselectionRequested = true;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
}
public void SetPriority(int Priority)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
BasePriority = Priority;
UpdatePriorityInheritance();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public long SetActivity(bool Pause)
{
long Result = 0;
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask;
+ ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask;
if (LowNibble != ThreadSchedState.Paused && LowNibble != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
{
if (Pause)
{
//Pause, the force pause flag should be clear (thread is NOT paused).
- if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) == 0)
+ if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0)
{
- ForcePauseFlags |= ThreadSchedState.ForcePauseFlag;
+ ForcePauseFlags |= ThreadSchedState.ThreadPauseFlag;
CombineForcePauseFlags();
}
@@ -400,17 +504,17 @@ namespace Ryujinx.HLE.HOS.Kernel
else
{
//Unpause, the force pause flag should be set (thread is paused).
- if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) != 0)
+ if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0)
{
ThreadSchedState OldForcePauseFlags = ForcePauseFlags;
- ForcePauseFlags &= ~ThreadSchedState.ForcePauseFlag;
+ ForcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag;
- if ((OldForcePauseFlags & ~ThreadSchedState.ForcePauseFlag) == ThreadSchedState.None)
+ if ((OldForcePauseFlags & ~ThreadSchedState.ThreadPauseFlag) == ThreadSchedState.None)
{
ThreadSchedState OldSchedFlags = SchedFlags;
- SchedFlags &= ThreadSchedState.LowNibbleMask;
+ SchedFlags &= ThreadSchedState.LowMask;
AdjustScheduling(OldSchedFlags);
}
@@ -422,27 +526,27 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
+ System.CriticalSection.Leave();
return Result;
}
public void CancelSynchronization()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.Paused || !WaitingSync)
+ if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.Paused || !WaitingSync)
{
SyncCancelled = true;
}
- else if (WithholderNode != null)
+ else if (Withholder != null)
{
- System.Withholders.Remove(WithholderNode);
+ Withholder.Remove(WithholderNode);
SetNewSchedFlags(ThreadSchedState.Running);
- WithholderNode = null;
+ Withholder = null;
SyncCancelled = true;
}
@@ -456,12 +560,12 @@ namespace Ryujinx.HLE.HOS.Kernel
SyncCancelled = false;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
- public long SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
+ public KernelResult SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
bool UseOverride = AffinityOverrideCount != 0;
@@ -472,9 +576,9 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((NewAffinityMask & (1 << NewCore)) == 0)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
- return MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
+ return KernelResult.InvalidCombination;
}
}
@@ -510,9 +614,9 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
- return 0;
+ return KernelResult.Success;
}
private static int HighestSetCore(long Mask)
@@ -531,7 +635,7 @@ namespace Ryujinx.HLE.HOS.Kernel
private void CombineForcePauseFlags()
{
ThreadSchedState OldFlags = SchedFlags;
- ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask;
+ ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask;
SchedFlags = LowNibble | ForcePauseFlags;
@@ -540,33 +644,33 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SetNewSchedFlags(ThreadSchedState NewFlags)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
ThreadSchedState OldFlags = SchedFlags;
- SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) | NewFlags;
+ SchedFlags = (OldFlags & ThreadSchedState.HighMask) | NewFlags;
- if ((OldFlags & ThreadSchedState.LowNibbleMask) != NewFlags)
+ if ((OldFlags & ThreadSchedState.LowMask) != NewFlags)
{
AdjustScheduling(OldFlags);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public void ReleaseAndResume()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- if ((SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused)
+ if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
{
- if (WithholderNode != null)
+ if (Withholder != null)
{
- System.Withholders.Remove(WithholderNode);
+ Withholder.Remove(WithholderNode);
SetNewSchedFlags(ThreadSchedState.Running);
- WithholderNode = null;
+ Withholder = null;
}
else
{
@@ -574,21 +678,21 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public void Reschedule(ThreadSchedState NewFlags)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
ThreadSchedState OldFlags = SchedFlags;
- SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) |
- (NewFlags & ThreadSchedState.LowNibbleMask);
+ SchedFlags = (OldFlags & ThreadSchedState.HighMask) |
+ (NewFlags & ThreadSchedState.LowMask);
AdjustScheduling(OldFlags);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public void AddMutexWaiter(KThread Requester)
@@ -866,18 +970,61 @@ namespace Ryujinx.HLE.HOS.Kernel
return HasExited;
}
+ public void SetEntryArguments(long ArgsPtr, int ThreadHandle)
+ {
+ Context.ThreadState.X0 = (ulong)ArgsPtr;
+ Context.ThreadState.X1 = (ulong)ThreadHandle;
+ }
+
public void ClearExclusive()
{
- Owner.Memory.ClearExclusive(CurrentCore);
+ Owner.CpuMemory.ClearExclusive(CurrentCore);
}
public void TimeUp()
{
- System.CriticalSectionLock.Lock();
+ ReleaseAndResume();
+ }
+
+ public void PrintGuestStackTrace()
+ {
+ Owner.Debugger.PrintGuestStackTrace(Context.ThreadState);
+ }
+
+ private void ThreadFinishedHandler(object sender, EventArgs e)
+ {
+ System.Scheduler.ExitThread(this);
+
+ Terminate();
+
+ System.Scheduler.RemoveThread(this);
+ }
+
+ public void Terminate()
+ {
+ Owner?.RemoveThread(this);
+
+ if (TlsAddress != 0 && Owner.FreeThreadLocalStorage(TlsAddress) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure freeing thread local storage.");
+ }
+
+ System.CriticalSection.Enter();
+
+ //Wake up all threads that may be waiting for a mutex being held
+ //by this thread.
+ foreach (KThread Thread in MutexWaiters)
+ {
+ Thread.MutexOwner = null;
+ Thread.PreferredCoreOverride = 0;
+ Thread.ObjSyncResult = 0xfa01;
+
+ Thread.ReleaseAndResume();
+ }
- SetNewSchedFlags(ThreadSchedState.Running);
+ System.CriticalSection.Leave();
- System.CriticalSectionLock.Unlock();
+ Owner?.DecrementThreadCountAndTerminateIfZero();
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs
index 47a3c86c..375789f0 100644
--- a/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs
@@ -1,6 +1,6 @@
+using Ryujinx.Common;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
using System.Threading;
@@ -25,18 +25,12 @@ namespace Ryujinx.HLE.HOS.Kernel
private AutoResetEvent WaitEvent;
- private Stopwatch Counter;
-
private bool KeepRunning;
public KTimeManager()
{
WaitingObjects = new List<WaitingObject>();
- Counter = new Stopwatch();
-
- Counter.Start();
-
KeepRunning = true;
Thread Work = new Thread(WaitAndCheckScheduledObjects);
@@ -46,26 +40,36 @@ namespace Ryujinx.HLE.HOS.Kernel
public void ScheduleFutureInvocation(IKFutureSchedulerObject Object, long Timeout)
{
+ long TimePoint = PerformanceCounter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
+
lock (WaitingObjects)
{
- long TimePoint = Counter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
-
WaitingObjects.Add(new WaitingObject(Object, TimePoint));
}
WaitEvent.Set();
}
- private long ConvertNanosecondsToMilliseconds(long Timeout)
+ public static long ConvertNanosecondsToMilliseconds(long Time)
{
- Timeout /= 1000000;
+ Time /= 1000000;
- if ((ulong)Timeout > int.MaxValue)
+ if ((ulong)Time > int.MaxValue)
{
return int.MaxValue;
}
- return Timeout;
+ return Time;
+ }
+
+ public static long ConvertMillisecondsToNanoseconds(long Time)
+ {
+ return Time * 1000000;
+ }
+
+ public static long ConvertMillisecondsToTicks(long Time)
+ {
+ return Time * 19200;
}
public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object)
@@ -82,26 +86,31 @@ namespace Ryujinx.HLE.HOS.Kernel
{
while (KeepRunning)
{
- Monitor.Enter(WaitingObjects);
-
- WaitingObject Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
+ WaitingObject Next;
- Monitor.Exit(WaitingObjects);
+ lock (WaitingObjects)
+ {
+ Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
+ }
if (Next != null)
{
- long TimePoint = Counter.ElapsedMilliseconds;
+ long TimePoint = PerformanceCounter.ElapsedMilliseconds;
if (Next.TimePoint > TimePoint)
{
WaitEvent.WaitOne((int)(Next.TimePoint - TimePoint));
}
- Monitor.Enter(WaitingObjects);
+ bool TimeUp = PerformanceCounter.ElapsedMilliseconds >= Next.TimePoint;
- bool TimeUp = Counter.ElapsedMilliseconds >= Next.TimePoint && WaitingObjects.Remove(Next);
-
- Monitor.Exit(WaitingObjects);
+ if (TimeUp)
+ {
+ lock (WaitingObjects)
+ {
+ TimeUp = WaitingObjects.Remove(Next);
+ }
+ }
if (TimeUp)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs b/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs
new file mode 100644
index 00000000..18dc2dec
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs
@@ -0,0 +1,73 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KTlsPageInfo
+ {
+ public const int TlsEntrySize = 0x200;
+
+ public ulong PageAddr { get; private set; }
+
+ private bool[] IsSlotFree;
+
+ public KTlsPageInfo(ulong PageAddress)
+ {
+ this.PageAddr = PageAddress;
+
+ IsSlotFree = new bool[KMemoryManager.PageSize / TlsEntrySize];
+
+ for (int Index = 0; Index < IsSlotFree.Length; Index++)
+ {
+ IsSlotFree[Index] = true;
+ }
+ }
+
+ public bool TryGetFreePage(out ulong Address)
+ {
+ Address = PageAddr;
+
+ for (int Index = 0; Index < IsSlotFree.Length; Index++)
+ {
+ if (IsSlotFree[Index])
+ {
+ IsSlotFree[Index] = false;
+
+ return true;
+ }
+
+ Address += TlsEntrySize;
+ }
+
+ Address = 0;
+
+ return false;
+ }
+
+ public bool IsFull()
+ {
+ bool HasFree = false;
+
+ for (int Index = 0; Index < IsSlotFree.Length; Index++)
+ {
+ HasFree |= IsSlotFree[Index];
+ }
+
+ return !HasFree;
+ }
+
+ public bool IsEmpty()
+ {
+ bool AllFree = true;
+
+ for (int Index = 0; Index < IsSlotFree.Length; Index++)
+ {
+ AllFree &= IsSlotFree[Index];
+ }
+
+ return AllFree;
+ }
+
+ public void FreeTlsSlot(ulong Address)
+ {
+ IsSlotFree[(Address - PageAddr) / TlsEntrySize] = true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs
index 6ebffa7e..5598f78d 100644
--- a/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs
@@ -2,13 +2,13 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class KTransferMemory
{
- public long Position { get; private set; }
- public long Size { get; private set; }
+ public ulong Address { get; private set; }
+ public ulong Size { get; private set; }
- public KTransferMemory(long Position, long Size)
+ public KTransferMemory(ulong Address, ulong Size)
{
- this.Position = Position;
- this.Size = Size;
+ this.Address = Address;
+ this.Size = Size;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelInit.cs b/Ryujinx.HLE/HOS/Kernel/KernelInit.cs
new file mode 100644
index 00000000..efb514c1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KernelInit.cs
@@ -0,0 +1,136 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ static class KernelInit
+ {
+ public static void InitializeResourceLimit(KResourceLimit ResourceLimit)
+ {
+ void EnsureSuccess(KernelResult Result)
+ {
+ if (Result != KernelResult.Success)
+ {
+ throw new InvalidOperationException($"Unexpected result \"{Result}\".");
+ }
+ }
+
+ int KernelMemoryCfg = 0;
+
+ long RamSize = GetRamSize(KernelMemoryCfg);
+
+ EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Memory, RamSize));
+ EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Thread, 800));
+ EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Event, 700));
+ EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.TransferMemory, 200));
+ EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Session, 900));
+
+ if (!ResourceLimit.Reserve(LimitableResource.Memory, 0) ||
+ !ResourceLimit.Reserve(LimitableResource.Memory, 0x60000))
+ {
+ throw new InvalidOperationException("Unexpected failure reserving memory on resource limit.");
+ }
+ }
+
+ public static KMemoryRegionManager[] GetMemoryRegions()
+ {
+ KMemoryArrange Arrange = GetMemoryArrange();
+
+ return new KMemoryRegionManager[]
+ {
+ GetMemoryRegion(Arrange.Application),
+ GetMemoryRegion(Arrange.Applet),
+ GetMemoryRegion(Arrange.Service),
+ GetMemoryRegion(Arrange.NvServices)
+ };
+ }
+
+ private static KMemoryRegionManager GetMemoryRegion(KMemoryArrangeRegion Region)
+ {
+ return new KMemoryRegionManager(Region.Address, Region.Size, Region.EndAddr);
+ }
+
+ private static KMemoryArrange GetMemoryArrange()
+ {
+ int McEmemCfg = 0x1000;
+
+ ulong EmemApertureSize = (ulong)(McEmemCfg & 0x3fff) << 20;
+
+ int KernelMemoryCfg = 0;
+
+ ulong RamSize = (ulong)GetRamSize(KernelMemoryCfg);
+
+ ulong RamPart0;
+ ulong RamPart1;
+
+ if (RamSize * 2 > EmemApertureSize)
+ {
+ RamPart0 = EmemApertureSize / 2;
+ RamPart1 = EmemApertureSize / 2;
+ }
+ else
+ {
+ RamPart0 = EmemApertureSize;
+ RamPart1 = 0;
+ }
+
+ int MemoryArrange = 1;
+
+ ulong ApplicationRgSize;
+
+ switch (MemoryArrange)
+ {
+ case 2: ApplicationRgSize = 0x80000000; break;
+ case 0x11:
+ case 0x21: ApplicationRgSize = 0x133400000; break;
+ default: ApplicationRgSize = 0xcd500000; break;
+ }
+
+ ulong AppletRgSize;
+
+ switch (MemoryArrange)
+ {
+ case 2: AppletRgSize = 0x61200000; break;
+ case 3: AppletRgSize = 0x1c000000; break;
+ case 0x11: AppletRgSize = 0x23200000; break;
+ case 0x12:
+ case 0x21: AppletRgSize = 0x89100000; break;
+ default: AppletRgSize = 0x1fb00000; break;
+ }
+
+ KMemoryArrangeRegion ServiceRg;
+ KMemoryArrangeRegion NvServicesRg;
+ KMemoryArrangeRegion AppletRg;
+ KMemoryArrangeRegion ApplicationRg;
+
+ const ulong NvServicesRgSize = 0x29ba000;
+
+ ulong ApplicationRgEnd = DramMemoryMap.DramEnd; //- RamPart0;
+
+ ApplicationRg = new KMemoryArrangeRegion(ApplicationRgEnd - ApplicationRgSize, ApplicationRgSize);
+
+ ulong NvServicesRgEnd = ApplicationRg.Address - AppletRgSize;
+
+ NvServicesRg = new KMemoryArrangeRegion(NvServicesRgEnd - NvServicesRgSize, NvServicesRgSize);
+ AppletRg = new KMemoryArrangeRegion(NvServicesRgEnd, AppletRgSize);
+
+ //Note: There is an extra region used by the kernel, however
+ //since we are doing HLE we are not going to use that memory, so give all
+ //the remaining memory space to services.
+ ulong ServiceRgSize = NvServicesRg.Address - DramMemoryMap.SlabHeapEnd;
+
+ ServiceRg = new KMemoryArrangeRegion(DramMemoryMap.SlabHeapEnd, ServiceRgSize);
+
+ return new KMemoryArrange(ServiceRg, NvServicesRg, AppletRg, ApplicationRg);
+ }
+
+ private static long GetRamSize(int KernelMemoryCfg)
+ {
+ switch ((KernelMemoryCfg >> 16) & 3)
+ {
+ case 1: return 0x180000000;
+ case 2: return 0x200000000;
+ default: return 0x100000000;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs
index d9cbfc67..9870d175 100644
--- a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs
@@ -2,9 +2,30 @@ namespace Ryujinx.HLE.HOS.Kernel
{
enum KernelResult
{
- Success = 0,
- HandleTableFull = 0xd201,
- InvalidHandle = 0xe401,
- InvalidState = 0xfa01
+ Success = 0,
+ InvalidCapability = 0x1c01,
+ ThreadTerminating = 0x7601,
+ InvalidSize = 0xca01,
+ InvalidAddress = 0xcc01,
+ OutOfResource = 0xce01,
+ OutOfMemory = 0xd001,
+ HandleTableFull = 0xd201,
+ InvalidMemState = 0xd401,
+ InvalidPermission = 0xd801,
+ InvalidMemRange = 0xdc01,
+ InvalidPriority = 0xe001,
+ InvalidCpuCore = 0xe201,
+ InvalidHandle = 0xe401,
+ UserCopyFailed = 0xe601,
+ InvalidCombination = 0xe801,
+ TimedOut = 0xea01,
+ Cancelled = 0xec01,
+ MaximumExceeded = 0xee01,
+ InvalidEnumValue = 0xf001,
+ NotFound = 0xf201,
+ InvalidThread = 0xf401,
+ InvalidState = 0xfa01,
+ ReservedValue = 0xfc01,
+ ResLimitExceeded = 0x10801
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs b/Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs
new file mode 100644
index 00000000..a3fabeae
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs
@@ -0,0 +1,71 @@
+using ChocolArm64.Memory;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ static class KernelTransfer
+ {
+ public static bool UserToKernelInt32(Horizon System, long Address, out int Value)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+ CurrentProcess.CpuMemory.IsMapped(Address + 3))
+ {
+ Value = CurrentProcess.CpuMemory.ReadInt32(Address);
+
+ return true;
+ }
+
+ Value = 0;
+
+ return false;
+ }
+
+ public static bool UserToKernelString(Horizon System, long Address, int Size, out string Value)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+ CurrentProcess.CpuMemory.IsMapped(Address + Size - 1))
+ {
+ Value = MemoryHelper.ReadAsciiString(CurrentProcess.CpuMemory, Address, Size);
+
+ return true;
+ }
+
+ Value = null;
+
+ return false;
+ }
+
+ public static bool KernelToUserInt32(Horizon System, long Address, int Value)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+ CurrentProcess.CpuMemory.IsMapped(Address + 3))
+ {
+ CurrentProcess.CpuMemory.WriteInt32ToSharedAddr(Address, Value);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public static bool KernelToUserInt64(Horizon System, long Address, long Value)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+ CurrentProcess.CpuMemory.IsMapped(Address + 7))
+ {
+ CurrentProcess.CpuMemory.WriteInt64(Address, Value);
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs b/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs
new file mode 100644
index 00000000..baab4222
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ enum LimitableResource : byte
+ {
+ Memory = 0,
+ Thread = 1,
+ Event = 2,
+ TransferMemory = 3,
+ Session = 4,
+
+ Count = 5
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs b/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs
new file mode 100644
index 00000000..b9350121
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ enum MemoryOperation
+ {
+ MapPa,
+ MapVa,
+ Allocate,
+ Unmap,
+ ChangePermRw,
+ ChangePermsAndAttributes
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs b/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs
new file mode 100644
index 00000000..ea4f33c9
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ enum MemoryRegion
+ {
+ Application = 0,
+ Applet = 1,
+ Service = 2,
+ NvServices = 3
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryState.cs b/Ryujinx.HLE/HOS/Kernel/MemoryState.cs
index 2c37723c..e2ce27ef 100644
--- a/Ryujinx.HLE/HOS/Kernel/MemoryState.cs
+++ b/Ryujinx.HLE/HOS/Kernel/MemoryState.cs
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel
ModCodeStatic = 0x00DD7E08,
ModCodeMutable = 0x03FFBD09,
IpcBuffer0 = 0x005C3C0A,
- MappedMemory = 0x005C3C0B,
+ Stack = 0x005C3C0B,
ThreadLocal = 0x0040200C,
TransferMemoryIsolated = 0x015C3C0D,
TransferMemory = 0x005C380E,
diff --git a/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs b/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs
new file mode 100644
index 00000000..b90d54d2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs
@@ -0,0 +1,128 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class MersenneTwister
+ {
+ private int Index;
+ private uint[] Mt;
+
+ public MersenneTwister(uint Seed)
+ {
+ Mt = new uint[624];
+
+ Mt[0] = Seed;
+
+ for (int MtIdx = 1; MtIdx < Mt.Length; MtIdx++)
+ {
+ uint Prev = Mt[MtIdx - 1];
+
+ Mt[MtIdx] = (uint)(0x6c078965 * (Prev ^ (Prev >> 30)) + MtIdx);
+ }
+
+ Index = Mt.Length;
+ }
+
+ public long GenRandomNumber(long Min, long Max)
+ {
+ long Range = Max - Min;
+
+ if (Min == Max)
+ {
+ return Min;
+ }
+
+ if (Range == -1)
+ {
+ //Increment would cause a overflow, special case.
+ return GenRandomNumber(2, 2, 32, 0xffffffffu, 0xffffffffu);
+ }
+
+ Range++;
+
+ //This is log2(Range) plus one.
+ int NextRangeLog2 = 64 - BitUtils.CountLeadingZeros64(Range);
+
+ //If Range is already power of 2, subtract one to use log2(Range) directly.
+ int RangeLog2 = NextRangeLog2 - (BitUtils.IsPowerOfTwo64(Range) ? 1 : 0);
+
+ int Parts = RangeLog2 > 32 ? 2 : 1;
+ int BitsPerPart = RangeLog2 / Parts;
+
+ int FullParts = Parts - (RangeLog2 - Parts * BitsPerPart);
+
+ uint Mask = 0xffffffffu >> (32 - BitsPerPart);
+ uint MaskPlus1 = 0xffffffffu >> (31 - BitsPerPart);
+
+ long RandomNumber;
+
+ do
+ {
+ RandomNumber = GenRandomNumber(Parts, FullParts, BitsPerPart, Mask, MaskPlus1);
+ }
+ while ((ulong)RandomNumber >= (ulong)Range);
+
+ return Min + RandomNumber;
+ }
+
+ private long GenRandomNumber(
+ int Parts,
+ int FullParts,
+ int BitsPerPart,
+ uint Mask,
+ uint MaskPlus1)
+ {
+ long RandomNumber = 0;
+
+ int Part = 0;
+
+ for (; Part < FullParts; Part++)
+ {
+ RandomNumber <<= BitsPerPart;
+ RandomNumber |= GenRandomNumber() & Mask;
+ }
+
+ for (; Part < Parts; Part++)
+ {
+ RandomNumber <<= BitsPerPart + 1;
+ RandomNumber |= GenRandomNumber() & MaskPlus1;
+ }
+
+ return RandomNumber;
+ }
+
+ private uint GenRandomNumber()
+ {
+ if (Index >= Mt.Length)
+ {
+ Twist();
+ }
+
+ uint Value = Mt[Index++];
+
+ Value ^= Value >> 11;
+ Value ^= (Value << 7) & 0x9d2c5680;
+ Value ^= (Value << 15) & 0xefc60000;
+ Value ^= Value >> 18;
+
+ return Value;
+ }
+
+ private void Twist()
+ {
+ for (int MtIdx = 0; MtIdx < Mt.Length; MtIdx++)
+ {
+ uint Value = (Mt[MtIdx] & 0x80000000) + (Mt[(MtIdx + 1) % Mt.Length] & 0x7fffffff);
+
+ Mt[MtIdx] = Mt[(MtIdx + 397) % Mt.Length] ^ (Value >> 1);
+
+ if ((Value & 1) != 0)
+ {
+ Mt[MtIdx] ^= 0x9908b0df;
+ }
+ }
+
+ Index = 0;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs b/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs
new file mode 100644
index 00000000..dae1345a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs
@@ -0,0 +1,37 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ struct ProcessCreationInfo
+ {
+ public string Name { get; private set; }
+
+ public int Category { get; private set; }
+ public long TitleId { get; private set; }
+
+ public ulong CodeAddress { get; private set; }
+ public int CodePagesCount { get; private set; }
+
+ public int MmuFlags { get; private set; }
+ public int ResourceLimitHandle { get; private set; }
+ public int PersonalMmHeapPagesCount { get; private set; }
+
+ public ProcessCreationInfo(
+ string Name,
+ int Category,
+ long TitleId,
+ ulong CodeAddress,
+ int CodePagesCount,
+ int MmuFlags,
+ int ResourceLimitHandle,
+ int PersonalMmHeapPagesCount)
+ {
+ this.Name = Name;
+ this.Category = Category;
+ this.TitleId = TitleId;
+ this.CodeAddress = CodeAddress;
+ this.CodePagesCount = CodePagesCount;
+ this.MmuFlags = MmuFlags;
+ this.ResourceLimitHandle = ResourceLimitHandle;
+ this.PersonalMmHeapPagesCount = PersonalMmHeapPagesCount;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/ProcessState.cs b/Ryujinx.HLE/HOS/Kernel/ProcessState.cs
new file mode 100644
index 00000000..98ff4207
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/ProcessState.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ enum ProcessState : byte
+ {
+ Created = 0,
+ CreatedAttached = 1,
+ Started = 2,
+ Crashed = 3,
+ Attached = 4,
+ Exiting = 5,
+ Exited = 6,
+ DebugSuspended = 7
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs
index 9b475d4e..cbc5e31c 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs
@@ -1,8 +1,8 @@
using ChocolArm64.Events;
using ChocolArm64.Memory;
using ChocolArm64.State;
-using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel
private Dictionary<int, SvcFunc> SvcFuncs;
private Switch Device;
- private Process Process;
+ private KProcess Process;
private Horizon System;
private MemoryManager Memory;
@@ -39,9 +39,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- private static Random Rng;
-
- public SvcHandler(Switch Device, Process Process)
+ public SvcHandler(Switch Device, KProcess Process)
{
SvcFuncs = new Dictionary<int, SvcFunc>()
{
@@ -51,14 +49,14 @@ namespace Ryujinx.HLE.HOS.Kernel
{ 0x05, SvcUnmapMemory },
{ 0x06, SvcQueryMemory },
{ 0x07, SvcExitProcess },
- { 0x08, SvcCreateThread },
+ { 0x08, CreateThread64 },
{ 0x09, SvcStartThread },
{ 0x0a, SvcExitThread },
{ 0x0b, SvcSleepThread },
{ 0x0c, SvcGetThreadPriority },
{ 0x0d, SvcSetThreadPriority },
{ 0x0e, SvcGetThreadCoreMask },
- { 0x0f, SvcSetThreadCoreMask },
+ { 0x0f, SetThreadCoreMask64 },
{ 0x10, SvcGetCurrentProcessorNumber },
{ 0x11, SignalEvent64 },
{ 0x12, ClearEvent64 },
@@ -77,36 +75,34 @@ namespace Ryujinx.HLE.HOS.Kernel
{ 0x1f, SvcConnectToNamedPort },
{ 0x21, SvcSendSyncRequest },
{ 0x22, SvcSendSyncRequestWithUserBuffer },
+ { 0x24, GetProcessId64 },
{ 0x25, SvcGetThreadId },
{ 0x26, SvcBreak },
{ 0x27, SvcOutputDebugString },
- { 0x29, SvcGetInfo },
+ { 0x29, GetInfo64 },
{ 0x2c, SvcMapPhysicalMemory },
{ 0x2d, SvcUnmapPhysicalMemory },
{ 0x32, SvcSetThreadActivity },
{ 0x33, SvcGetThreadContext3 },
{ 0x34, SvcWaitForAddress },
{ 0x35, SvcSignalToAddress },
- { 0x45, CreateEvent64 }
+ { 0x45, CreateEvent64 },
+ { 0x65, GetProcessList64 },
+ { 0x6f, GetSystemInfo64 },
+ { 0x70, CreatePort64 },
+ { 0x71, ManageNamedPort64 }
};
this.Device = Device;
this.Process = Process;
- this.System = Process.Device.System;
- this.Memory = Process.Memory;
- }
-
- static SvcHandler()
- {
- Rng = new Random();
+ this.System = Device.System;
+ this.Memory = Process.CpuMemory;
}
public void SvcCall(object sender, InstExceptionEventArgs e)
{
CpuThreadState ThreadState = (CpuThreadState)sender;
- Process.GetThread(ThreadState.Tpidr).LastPc = e.Position;
-
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
{
Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called.");
@@ -117,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else
{
- Process.PrintStackTrace(ThreadState);
+ //Process.PrintStackTrace(ThreadState);
throw new NotImplementedException($"0x{e.Id:x4}");
}
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs
index 560ad4b3..b5845f0b 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs
@@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
ulong Size = ThreadState.X1;
- if ((Size & 0xFFFFFFFE001FFFFF) != 0)
+ if ((Size & 0xfffffffe001fffff) != 0)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!");
@@ -20,24 +20,24 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- long Result = Process.MemoryManager.TrySetHeapSize((long)Size, out long Position);
+ KernelResult Result = Process.MemoryManager.SetHeapSize(Size, out ulong Position);
ThreadState.X0 = (ulong)Result;
- if (Result == 0)
+ if (Result == KernelResult.Success)
{
- ThreadState.X1 = (ulong)Position;
+ ThreadState.X1 = Position;
}
else
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
}
private void SvcSetMemoryAttribute(CpuThreadState ThreadState)
{
- long Position = (long)ThreadState.X0;
- long Size = (long)ThreadState.X1;
+ ulong Position = ThreadState.X0;
+ ulong Size = ThreadState.X1;
if (!PageAligned(Position))
{
@@ -72,19 +72,19 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- long Result = Process.MemoryManager.SetMemoryAttribute(
+ KernelResult Result = Process.MemoryManager.SetMemoryAttribute(
Position,
Size,
AttributeMask,
AttributeValue);
- if (Result != 0)
+ if (Result != KernelResult.Success)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
else
{
- Memory.StopObservingRegion(Position, Size);
+ Memory.StopObservingRegion((long)Position, (long)Size);
}
ThreadState.X0 = (ulong)Result;
@@ -92,9 +92,9 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcMapMemory(CpuThreadState ThreadState)
{
- long Dst = (long)ThreadState.X0;
- long Src = (long)ThreadState.X1;
- long Size = (long)ThreadState.X2;
+ ulong Dst = ThreadState.X0;
+ ulong Src = ThreadState.X1;
+ ulong Size = ThreadState.X2;
if (!PageAligned(Src | Dst))
{
@@ -114,7 +114,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
+ if (Src + Size <= Src || Dst + Size <= Dst)
{
Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
@@ -123,7 +123,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if (!InsideAddrSpace(Src, Size))
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (!CurrentProcess.MemoryManager.InsideAddrSpace(Src, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
@@ -132,7 +134,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if (!InsideNewMapRegion(Dst, Size))
+ if (CurrentProcess.MemoryManager.OutsideStackRegion(Dst, Size) ||
+ CurrentProcess.MemoryManager.InsideHeapRegion (Dst, Size) ||
+ CurrentProcess.MemoryManager.InsideAliasRegion (Dst, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
@@ -141,9 +145,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- long Result = Process.MemoryManager.Map(Src, Dst, Size);
+ KernelResult Result = Process.MemoryManager.Map(Dst, Src, Size);
- if (Result != 0)
+ if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
@@ -153,9 +157,9 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcUnmapMemory(CpuThreadState ThreadState)
{
- long Dst = (long)ThreadState.X0;
- long Src = (long)ThreadState.X1;
- long Size = (long)ThreadState.X2;
+ ulong Dst = ThreadState.X0;
+ ulong Src = ThreadState.X1;
+ ulong Size = ThreadState.X2;
if (!PageAligned(Src | Dst))
{
@@ -175,7 +179,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
+ if (Src + Size <= Src || Dst + Size <= Dst)
{
Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
@@ -184,7 +188,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if (!InsideAddrSpace(Src, Size))
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (!CurrentProcess.MemoryManager.InsideAddrSpace(Src, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
@@ -193,7 +199,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if (!InsideNewMapRegion(Dst, Size))
+ if (CurrentProcess.MemoryManager.OutsideStackRegion(Dst, Size) ||
+ CurrentProcess.MemoryManager.InsideHeapRegion (Dst, Size) ||
+ CurrentProcess.MemoryManager.InsideAliasRegion (Dst, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
@@ -202,9 +210,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- long Result = Process.MemoryManager.Unmap(Src, Dst, Size);
+ KernelResult Result = Process.MemoryManager.Unmap(Dst, Src, Size);
- if (Result != 0)
+ if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
@@ -214,19 +222,19 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcQueryMemory(CpuThreadState ThreadState)
{
- long InfoPtr = (long)ThreadState.X0;
- long Position = (long)ThreadState.X2;
+ long InfoPtr = (long)ThreadState.X0;
+ ulong Position = ThreadState.X2;
KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position);
- Memory.WriteInt64(InfoPtr + 0x00, BlkInfo.Position);
- Memory.WriteInt64(InfoPtr + 0x08, BlkInfo.Size);
- Memory.WriteInt32(InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
- Memory.WriteInt32(InfoPtr + 0x14, (int)BlkInfo.Attribute);
- Memory.WriteInt32(InfoPtr + 0x18, (int)BlkInfo.Permission);
- Memory.WriteInt32(InfoPtr + 0x1c, BlkInfo.IpcRefCount);
- Memory.WriteInt32(InfoPtr + 0x20, BlkInfo.DeviceRefCount);
- Memory.WriteInt32(InfoPtr + 0x24, 0);
+ Memory.WriteUInt64(InfoPtr + 0x00, BlkInfo.Address);
+ Memory.WriteUInt64(InfoPtr + 0x08, BlkInfo.Size);
+ Memory.WriteInt32 (InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
+ Memory.WriteInt32 (InfoPtr + 0x14, (int)BlkInfo.Attribute);
+ Memory.WriteInt32 (InfoPtr + 0x18, (int)BlkInfo.Permission);
+ Memory.WriteInt32 (InfoPtr + 0x1c, BlkInfo.IpcRefCount);
+ Memory.WriteInt32 (InfoPtr + 0x20, BlkInfo.DeviceRefCount);
+ Memory.WriteInt32 (InfoPtr + 0x24, 0);
ThreadState.X0 = 0;
ThreadState.X1 = 0;
@@ -234,13 +242,13 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcMapSharedMemory(CpuThreadState ThreadState)
{
- int Handle = (int)ThreadState.X0;
- long Position = (long)ThreadState.X1;
- long Size = (long)ThreadState.X2;
+ int Handle = (int)ThreadState.X0;
+ ulong Address = ThreadState.X1;
+ ulong Size = ThreadState.X2;
- if (!PageAligned(Position))
+ if (!PageAligned(Address))
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@@ -256,9 +264,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if ((ulong)(Position + Size) <= (ulong)Position)
+ if (Address + Size <= Address)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@@ -276,7 +284,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject<KSharedMemory>(Handle);
if (SharedMemory == null)
{
@@ -287,29 +297,27 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
+ if (CurrentProcess.MemoryManager.IsInvalidRegion (Address, Size) ||
+ CurrentProcess.MemoryManager.InsideHeapRegion (Address, Size) ||
+ CurrentProcess.MemoryManager.InsideAliasRegion(Address, Size))
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
- if (SharedMemory.Size != Size)
- {
- Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!");
-
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
-
- return;
- }
-
- long Result = Process.MemoryManager.MapSharedMemory(SharedMemory, Permission, Position);
+ KernelResult Result = SharedMemory.MapIntoProcess(
+ CurrentProcess.MemoryManager,
+ Address,
+ Size,
+ CurrentProcess,
+ Permission);
- if (Result != 0)
+ if (Result != KernelResult.Success)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
ThreadState.X0 = (ulong)Result;
@@ -317,13 +325,13 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcUnmapSharedMemory(CpuThreadState ThreadState)
{
- int Handle = (int)ThreadState.X0;
- long Position = (long)ThreadState.X1;
- long Size = (long)ThreadState.X2;
+ int Handle = (int)ThreadState.X0;
+ ulong Address = ThreadState.X1;
+ ulong Size = ThreadState.X2;
- if (!PageAligned(Position))
+ if (!PageAligned(Address))
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@@ -339,16 +347,18 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if ((ulong)(Position + Size) <= (ulong)Position)
+ if (Address + Size <= Address)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
- KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject<KSharedMemory>(Handle);
if (SharedMemory == null)
{
@@ -359,20 +369,26 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
+ if (CurrentProcess.MemoryManager.IsInvalidRegion (Address, Size) ||
+ CurrentProcess.MemoryManager.InsideHeapRegion (Address, Size) ||
+ CurrentProcess.MemoryManager.InsideAliasRegion(Address, Size))
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
- long Result = Process.MemoryManager.UnmapSharedMemory(Position, Size);
+ KernelResult Result = SharedMemory.UnmapFromProcess(
+ CurrentProcess.MemoryManager,
+ Address,
+ Size,
+ CurrentProcess);
- if (Result != 0)
+ if (Result != KernelResult.Success)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
ThreadState.X0 = (ulong)Result;
@@ -380,12 +396,12 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcCreateTransferMemory(CpuThreadState ThreadState)
{
- long Position = (long)ThreadState.X1;
- long Size = (long)ThreadState.X2;
+ ulong Address = ThreadState.X1;
+ ulong Size = ThreadState.X2;
- if (!PageAligned(Position))
+ if (!PageAligned(Address))
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@@ -401,9 +417,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if ((ulong)(Position + Size) <= (ulong)Position)
+ if (Address + Size <= Address)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@@ -421,9 +437,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- Process.MemoryManager.ReserveTransferMemory(Position, Size, Permission);
+ Process.MemoryManager.ReserveTransferMemory(Address, Size, Permission);
- KTransferMemory TransferMemory = new KTransferMemory(Position, Size);
+ KTransferMemory TransferMemory = new KTransferMemory(Address, Size);
KernelResult Result = Process.HandleTable.GenerateHandle(TransferMemory, out int Handle);
@@ -433,12 +449,12 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcMapPhysicalMemory(CpuThreadState ThreadState)
{
- long Position = (long)ThreadState.X0;
- long Size = (long)ThreadState.X1;
+ ulong Address = ThreadState.X0;
+ ulong Size = ThreadState.X1;
- if (!PageAligned(Position))
+ if (!PageAligned(Address))
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@@ -454,27 +470,39 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if ((ulong)(Position + Size) <= (ulong)Position)
+ if (Address + Size <= Address)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
- if (!InsideAddrSpace(Position, Size))
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if ((CurrentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"System resource size is zero.");
+
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
+
+ return;
+ }
+
+ if (!CurrentProcess.MemoryManager.InsideAddrSpace (Address, Size) ||
+ CurrentProcess.MemoryManager.OutsideAliasRegion(Address, Size))
+ {
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Address:x16}.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
- long Result = Process.MemoryManager.MapPhysicalMemory(Position, Size);
+ KernelResult Result = Process.MemoryManager.MapPhysicalMemory(Address, Size);
- if (Result != 0)
+ if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
@@ -484,12 +512,12 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcUnmapPhysicalMemory(CpuThreadState ThreadState)
{
- long Position = (long)ThreadState.X0;
- long Size = (long)ThreadState.X1;
+ ulong Address = ThreadState.X0;
+ ulong Size = ThreadState.X1;
- if (!PageAligned(Position))
+ if (!PageAligned(Address))
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@@ -505,27 +533,39 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if ((ulong)(Position + Size) <= (ulong)Position)
+ if (Address + Size <= Address)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
- if (!InsideAddrSpace(Position, Size))
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if ((CurrentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
+ {
+ Logger.PrintWarning(LogClass.KernelSvc, $"System resource size is zero.");
+
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
+
+ return;
+ }
+
+ if (!CurrentProcess.MemoryManager.InsideAddrSpace (Address, Size) ||
+ CurrentProcess.MemoryManager.OutsideAliasRegion(Address, Size))
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Address:x16}.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
- long Result = Process.MemoryManager.UnmapPhysicalMemory(Position, Size);
+ KernelResult Result = Process.MemoryManager.UnmapPhysicalMemory(Address, Size);
- if (Result != 0)
+ if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
@@ -533,45 +573,9 @@ namespace Ryujinx.HLE.HOS.Kernel
ThreadState.X0 = (ulong)Result;
}
- private static bool PageAligned(long Position)
+ private static bool PageAligned(ulong Position)
{
return (Position & (KMemoryManager.PageSize - 1)) == 0;
}
-
- private bool InsideAddrSpace(long Position, long Size)
- {
- ulong Start = (ulong)Position;
- ulong End = (ulong)Size + Start;
-
- return Start >= (ulong)Process.MemoryManager.AddrSpaceStart &&
- End < (ulong)Process.MemoryManager.AddrSpaceEnd;
- }
-
- private bool InsideMapRegion(long Position, long Size)
- {
- ulong Start = (ulong)Position;
- ulong End = (ulong)Size + Start;
-
- return Start >= (ulong)Process.MemoryManager.MapRegionStart &&
- End < (ulong)Process.MemoryManager.MapRegionEnd;
- }
-
- private bool InsideHeapRegion(long Position, long Size)
- {
- ulong Start = (ulong)Position;
- ulong End = (ulong)Size + Start;
-
- return Start >= (ulong)Process.MemoryManager.HeapRegionStart &&
- End < (ulong)Process.MemoryManager.HeapRegionEnd;
- }
-
- private bool InsideNewMapRegion(long Position, long Size)
- {
- ulong Start = (ulong)Position;
- ulong End = (ulong)Size + Start;
-
- return Start >= (ulong)Process.MemoryManager.NewMapRegionStart &&
- End < (ulong)Process.MemoryManager.NewMapRegionEnd;
- }
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs
index 54aef5d7..1c1d76f1 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs
@@ -1,5 +1,6 @@
using ChocolArm64.Memory;
using ChocolArm64.State;
+using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
@@ -13,13 +14,9 @@ namespace Ryujinx.HLE.HOS.Kernel
{
partial class SvcHandler
{
- private const int AllowedCpuIdBitmask = 0b1111;
-
- private const bool EnableProcessDebugging = false;
-
private void SvcExitProcess(CpuThreadState ThreadState)
{
- Device.System.ExitProcess(Process.ProcessId);
+ System.Scheduler.GetCurrentProcess().Terminate();
}
private void SignalEvent64(CpuThreadState ThreadState)
@@ -106,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Kernel
else if (Obj is KTransferMemory TransferMemory)
{
Process.MemoryManager.ResetTransferMemory(
- TransferMemory.Position,
+ TransferMemory.Address,
TransferMemory.Size);
}
@@ -120,18 +117,28 @@ namespace Ryujinx.HLE.HOS.Kernel
private KernelResult ResetSignal(int Handle)
{
- KReadableEvent ReadableEvent = Process.HandleTable.GetObject<KReadableEvent>(Handle);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ KReadableEvent ReadableEvent = CurrentProcess.HandleTable.GetObject<KReadableEvent>(Handle);
KernelResult Result;
- //TODO: KProcess support.
if (ReadableEvent != null)
{
Result = ReadableEvent.ClearIfSignaled();
}
else
{
- Result = KernelResult.InvalidHandle;
+ KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
+
+ if (Process != null)
+ {
+ Result = Process.ClearIfNotExited();
+ }
+ else
+ {
+ Result = KernelResult.InvalidHandle;
+ }
}
if (Result == KernelResult.InvalidState)
@@ -187,17 +194,13 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SendSyncRequest(CpuThreadState ThreadState, long MessagePtr, long Size, int Handle)
{
- KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
-
byte[] MessageData = Memory.ReadBytes(MessagePtr, Size);
KSession Session = Process.HandleTable.GetObject<KSession>(Handle);
if (Session != null)
{
- //Process.Scheduler.Suspend(CurrThread);
-
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
KThread CurrentThread = System.Scheduler.GetCurrentThread();
@@ -214,7 +217,9 @@ namespace Ryujinx.HLE.HOS.Kernel
Message,
MessagePtr));
- System.CriticalSectionLock.Unlock();
+ System.ThreadCounter.AddCount();
+
+ System.CriticalSection.Leave();
ThreadState.X0 = (ulong)CurrentThread.ObjSyncResult;
}
@@ -238,25 +243,65 @@ namespace Ryujinx.HLE.HOS.Kernel
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)
{
long Reason = (long)ThreadState.X0;
long Unknown = (long)ThreadState.X1;
long Info = (long)ThreadState.X2;
+ KThread CurrentThread = System.Scheduler.GetCurrentThread();
+
if ((Reason & (1 << 31)) == 0)
{
- Process.PrintStackTrace(ThreadState);
+ CurrentThread.PrintGuestStackTrace();
throw new GuestBrokeExecutionException();
}
else
{
- Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered");
- Process.PrintStackTrace(ThreadState);
+ Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered.");
+
+ CurrentThread.PrintGuestStackTrace();
}
}
@@ -272,98 +317,243 @@ namespace Ryujinx.HLE.HOS.Kernel
ThreadState.X0 = 0;
}
- private void SvcGetInfo(CpuThreadState ThreadState)
+ private void GetInfo64(CpuThreadState ThreadState)
{
long StackPtr = (long)ThreadState.X0;
- int InfoType = (int)ThreadState.X1;
- long Handle = (long)ThreadState.X2;
- int InfoId = (int)ThreadState.X3;
+ uint Id = (uint)ThreadState.X1;
+ int Handle = (int)ThreadState.X2;
+ long SubId = (long)ThreadState.X3;
- //Fail for info not available on older Kernel versions.
- if (InfoType == 18 ||
- InfoType == 19 ||
- InfoType == 20 ||
- InfoType == 21 ||
- InfoType == 22)
- {
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
+ KernelResult Result = GetInfo(Id, Handle, SubId, out long Value);
- return;
- }
+ ThreadState.X0 = (ulong)Result;
+ ThreadState.X1 = (ulong)Value;
+ }
+
+ private KernelResult GetInfo(uint Id, int Handle, long SubId, out long Value)
+ {
+ Value = 0;
- switch (InfoType)
+ switch (Id)
{
case 0:
- ThreadState.X1 = AllowedCpuIdBitmask;
- break;
-
+ case 1:
case 2:
- ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionStart;
- break;
-
case 3:
- ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionEnd -
- (ulong)Process.MemoryManager.MapRegionStart;
- break;
-
case 4:
- ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionStart;
- break;
-
case 5:
- ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionEnd -
- (ulong)Process.MemoryManager.HeapRegionStart;
- break;
-
case 6:
- ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalAvailableSize;
- break;
-
case 7:
- ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalUsedSize;
+ 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:
- ThreadState.X1 = EnableProcessDebugging ? 1 : 0;
- break;
+ {
+ if (Handle != 0)
+ {
+ return KernelResult.InvalidHandle;
+ }
- case 11:
- ThreadState.X1 = (ulong)Rng.Next() + ((ulong)Rng.Next() << 32);
- break;
+ if (SubId != 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
- case 12:
- ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceStart;
- break;
+ Value = System.Scheduler.GetCurrentProcess().Debug ? 1 : 0;
- case 13:
- ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceEnd -
- (ulong)Process.MemoryManager.AddrSpaceStart;
break;
+ }
- case 14:
- ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionStart;
- 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;
+ }
- case 15:
- ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionEnd -
- (ulong)Process.MemoryManager.NewMapRegionStart;
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;
- case 16:
- ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0);
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];
- case 17:
- ThreadState.X1 = (ulong)Process.MemoryManager.PersonalMmHeapUsage;
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;
+ }
- default:
- Process.PrintStackTrace(ThreadState);
+ 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;
+ }
- throw new NotImplementedException($"SvcGetInfo: {InfoType} 0x{Handle:x8} {InfoId}");
+ default: return KernelResult.InvalidEnumValue;
}
- ThreadState.X0 = 0;
+ return KernelResult.Success;
}
private void CreateEvent64(CpuThreadState State)
@@ -397,5 +587,241 @@ namespace Ryujinx.HLE.HOS.Kernel
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;
+ }
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs
index 53a557de..ded8f8dc 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs
@@ -7,46 +7,82 @@ namespace Ryujinx.HLE.HOS.Kernel
{
partial class SvcHandler
{
- private void SvcCreateThread(CpuThreadState ThreadState)
+ private void CreateThread64(CpuThreadState ThreadState)
{
- long EntryPoint = (long)ThreadState.X1;
- long ArgsPtr = (long)ThreadState.X2;
- long StackTop = (long)ThreadState.X3;
- int Priority = (int)ThreadState.X4;
- int ProcessorId = (int)ThreadState.X5;
+ ulong Entrypoint = ThreadState.X1;
+ ulong ArgsPtr = ThreadState.X2;
+ ulong StackTop = ThreadState.X3;
+ int Priority = (int)ThreadState.X4;
+ int CpuCore = (int)ThreadState.X5;
- if ((uint)Priority > 0x3f)
- {
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!");
+ KernelResult Result = CreateThread(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, out int Handle);
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPriority);
+ ThreadState.X0 = (ulong)Result;
+ ThreadState.X1 = (ulong)Handle;
+ }
- return;
+ private KernelResult CreateThread(
+ ulong Entrypoint,
+ ulong ArgsPtr,
+ ulong StackTop,
+ int Priority,
+ int CpuCore,
+ out int Handle)
+ {
+ Handle = 0;
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CpuCore == -2)
+ {
+ CpuCore = CurrentProcess.DefaultCpuCore;
}
- if (ProcessorId == -2)
+ if ((uint)CpuCore >= KScheduler.CpuCoresCount || !CurrentProcess.IsCpuCoreAllowed(CpuCore))
{
- //TODO: Get this value from the NPDM file.
- ProcessorId = 0;
+ return KernelResult.InvalidCpuCore;
}
- else if ((uint)ProcessorId > 3)
+
+ if ((uint)Priority >= KScheduler.PrioritiesCount || !CurrentProcess.IsPriorityAllowed(Priority))
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!");
+ return KernelResult.InvalidPriority;
+ }
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
+ long Timeout = KTimeManager.ConvertMillisecondsToNanoseconds(100);
- return;
+ if (CurrentProcess.ResourceLimit != null &&
+ !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1, Timeout))
+ {
+ return KernelResult.ResLimitExceeded;
}
- int Handle = Process.MakeThread(
- EntryPoint,
- StackTop,
+ KThread Thread = new KThread(System);
+
+ KernelResult Result = CurrentProcess.InitializeThread(
+ Thread,
+ Entrypoint,
ArgsPtr,
+ StackTop,
Priority,
- ProcessorId);
+ CpuCore);
- ThreadState.X0 = 0;
- ThreadState.X1 = (ulong)Handle;
+ if (Result != KernelResult.Success)
+ {
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
+
+ return Result;
+ }
+
+ Result = Process.HandleTable.GenerateHandle(Thread, out Handle);
+
+ if (Result != KernelResult.Success)
+ {
+ Thread.Terminate();
+
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
+ }
+
+ return Result;
}
private void SvcStartThread(CpuThreadState ThreadState)
@@ -57,11 +93,11 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Thread != null)
{
- long Result = Thread.Start();
+ KernelResult Result = Thread.Start();
- if (Result != 0)
+ if (Result != KernelResult.Success)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
ThreadState.X0 = (ulong)Result;
@@ -78,9 +114,9 @@ namespace Ryujinx.HLE.HOS.Kernel
{
KThread CurrentThread = System.Scheduler.GetCurrentThread();
- CurrentThread.Exit();
+ System.Scheduler.ExitThread(CurrentThread);
- System.Scheduler.StopThread(CurrentThread);
+ CurrentThread.Exit();
}
private void SvcSleepThread(CpuThreadState ThreadState)
@@ -176,46 +212,60 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- private void SvcSetThreadCoreMask(CpuThreadState ThreadState)
+ private void SetThreadCoreMask64(CpuThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
- int PrefferedCore = (int)ThreadState.X1;
+ int PreferredCore = (int)ThreadState.X1;
long AffinityMask = (long)ThreadState.X2;
Logger.PrintDebug(LogClass.KernelSvc,
"Handle = 0x" + Handle .ToString("x8") + ", " +
- "PrefferedCore = 0x" + PrefferedCore.ToString("x8") + ", " +
+ "PreferredCore = 0x" + PreferredCore.ToString("x8") + ", " +
"AffinityMask = 0x" + AffinityMask .ToString("x16"));
- if (PrefferedCore == -2)
+ KernelResult Result = SetThreadCoreMask(Handle, PreferredCore, AffinityMask);
+
+ if (Result != KernelResult.Success)
{
- //TODO: Get this value from the NPDM file.
- PrefferedCore = 0;
+ Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
+ }
- AffinityMask = 1 << PrefferedCore;
+ ThreadState.X0 = (ulong)Result;
+ }
+
+ private KernelResult SetThreadCoreMask(int Handle, int PreferredCore, long AffinityMask)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (PreferredCore == -2)
+ {
+ PreferredCore = CurrentProcess.DefaultCpuCore;
+
+ AffinityMask = 1 << PreferredCore;
}
else
{
- //TODO: Check allowed cores from NPDM file.
-
- if ((uint)PrefferedCore > 3)
+ if ((CurrentProcess.Capabilities.AllowedCpuCoresMask | AffinityMask) !=
+ CurrentProcess.Capabilities.AllowedCpuCoresMask)
{
- if ((PrefferedCore | 2) != -1)
- {
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!");
+ return KernelResult.InvalidCpuCore;
+ }
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
+ if (AffinityMask == 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
- return;
+ if ((uint)PreferredCore > 3)
+ {
+ if ((PreferredCore | 2) != -1)
+ {
+ return KernelResult.InvalidCpuCore;
}
}
- else if ((AffinityMask & (1 << PrefferedCore)) == 0)
+ else if ((AffinityMask & (1 << PreferredCore)) == 0)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{AffinityMask:x8}!");
-
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
-
- return;
+ return KernelResult.InvalidCombination;
}
}
@@ -223,26 +273,15 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Thread == null)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
-
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
-
- return;
- }
-
- long Result = Thread.SetCoreAndAffinityMask(PrefferedCore, AffinityMask);
-
- if (Result != 0)
- {
- Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ return KernelResult.InvalidHandle;
}
- ThreadState.X0 = (ulong)Result;
+ return Thread.SetCoreAndAffinityMask(PreferredCore, AffinityMask);
}
private void SvcGetCurrentProcessorNumber(CpuThreadState ThreadState)
{
- ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).CurrentCore;
+ ThreadState.X0 = (ulong)System.Scheduler.GetCurrentThread().CurrentCore;
}
private void SvcGetThreadId(CpuThreadState ThreadState)
@@ -254,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Thread != null)
{
ThreadState.X0 = 0;
- ThreadState.X1 = (ulong)Thread.ThreadId;
+ ThreadState.X1 = (ulong)Thread.ThreadUid;
}
else
{
@@ -280,15 +319,24 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if (Thread.Owner != Process)
+ if (Thread.Owner != System.Scheduler.GetCurrentProcess())
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread owner process!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
}
+ if (Thread == System.Scheduler.GetCurrentThread())
+ {
+ Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
+
+ ThreadState.X0 = (ulong)KernelResult.InvalidThread;
+
+ return;
+ }
+
long Result = Thread.SetActivity(Pause);
if (Result != 0)
@@ -304,6 +352,9 @@ namespace Ryujinx.HLE.HOS.Kernel
long Position = (long)ThreadState.X0;
int Handle = (int)ThreadState.X1;
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+ KThread CurrentThread = System.Scheduler.GetCurrentThread();
+
KThread Thread = Process.HandleTable.GetObject<KThread>(Handle);
if (Thread == null)
@@ -315,9 +366,18 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if (Process.GetThread(ThreadState.Tpidr) == Thread)
+ if (Thread.Owner != CurrentProcess)
+ {
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
+
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+ return;
+ }
+
+ if (CurrentThread == Thread)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!");
+ Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread);
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
index 318bd290..3935df5d 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
@@ -32,6 +32,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
+ Logger.PrintDebug(LogClass.KernelSvc, $"Sync handle 0x{Handle:x8}");
+
KSynchronizationObject SyncObj = Process.HandleTable.GetObject<KSynchronizationObject>(Handle);
if (SyncObj == null)
@@ -116,12 +118,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- long Result = System.AddressArbiter.ArbitrateLock(
- Process,
- Memory,
- OwnerHandle,
- MutexAddress,
- RequesterHandle);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ long Result = CurrentProcess.AddressArbiter.ArbitrateLock(OwnerHandle, MutexAddress, RequesterHandle);
if (Result != 0)
{
@@ -155,7 +154,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- long Result = System.AddressArbiter.ArbitrateUnlock(Memory, MutexAddress);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ long Result = CurrentProcess.AddressArbiter.ArbitrateUnlock(MutexAddress);
if (Result != 0)
{
@@ -196,8 +197,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- long Result = System.AddressArbiter.WaitProcessWideKeyAtomic(
- Memory,
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ long Result = CurrentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
MutexAddress,
CondVarAddress,
ThreadHandle,
@@ -227,7 +229,9 @@ namespace Ryujinx.HLE.HOS.Kernel
"Address = 0x" + Address.ToString("x16") + ", " +
"Count = 0x" + Count .ToString("x8"));
- System.AddressArbiter.SignalProcessWideKey(Process, Memory, Address, Count);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ CurrentProcess.AddressArbiter.SignalProcessWideKey(Address, Count);
ThreadState.X0 = 0;
}
@@ -263,20 +267,22 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
long Result;
switch (Type)
{
case ArbitrationType.WaitIfLessThan:
- Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, false, Timeout);
+ Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, false, Timeout);
break;
case ArbitrationType.DecrementAndWaitIfLessThan:
- Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, true, Timeout);
+ Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, true, Timeout);
break;
case ArbitrationType.WaitIfEqual:
- Result = System.AddressArbiter.WaitForAddressIfEqual(Memory, Address, Value, Timeout);
+ Result = CurrentProcess.AddressArbiter.WaitForAddressIfEqual(Address, Value, Timeout);
break;
default:
@@ -323,20 +329,22 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
long Result;
switch (Type)
{
case SignalType.Signal:
- Result = System.AddressArbiter.Signal(Address, Count);
+ Result = CurrentProcess.AddressArbiter.Signal(Address, Count);
break;
case SignalType.SignalAndIncrementIfEqual:
- Result = System.AddressArbiter.SignalAndIncrementIfEqual(Memory, Address, Value, Count);
+ Result = CurrentProcess.AddressArbiter.SignalAndIncrementIfEqual(Address, Value, Count);
break;
case SignalType.SignalAndModifyIfEqual:
- Result = System.AddressArbiter.SignalAndModifyIfEqual(Memory, Address, Value, Count);
+ Result = CurrentProcess.AddressArbiter.SignalAndModifyIfEqual(Address, Value, Count);
break;
default:
diff --git a/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs b/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs
index 603446f3..37e5908a 100644
--- a/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs
+++ b/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs
@@ -1,11 +1,15 @@
namespace Ryujinx.HLE.HOS.Kernel
{
- enum ThreadSchedState : byte
+ enum ThreadSchedState : ushort
{
- LowNibbleMask = 0xf,
- HighNibbleMask = 0xf0,
- ExceptionalMask = 0x70,
- ForcePauseFlag = 0x20,
+ LowMask = 0xf,
+ HighMask = 0xfff0,
+ ForcePauseMask = 0x70,
+
+ ProcessPauseFlag = 1 << 4,
+ ThreadPauseFlag = 1 << 5,
+ ProcessDebugPauseFlag = 1 << 6,
+ KernelInitPauseFlag = 1 << 8,
None = 0,
Paused = 1,
diff --git a/Ryujinx.HLE/HOS/Kernel/ThreadType.cs b/Ryujinx.HLE/HOS/Kernel/ThreadType.cs
new file mode 100644
index 00000000..0fe83423
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/ThreadType.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ enum ThreadType
+ {
+ Dummy,
+ Kernel,
+ Kernel2,
+ User
+ }
+} \ No newline at end of file