aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/HOS/Kernel/Common
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Kernel/Common')
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs7
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs73
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs188
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs35
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs78
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs218
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs89
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs73
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs13
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs12
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs9
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs128
12 files changed, 923 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs
new file mode 100644
index 00000000..473683ff
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ interface IKFutureSchedulerObject
+ {
+ void TimeUp();
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
new file mode 100644
index 00000000..424bf788
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
@@ -0,0 +1,73 @@
+using Ryujinx.Horizon.Common;
+using System.Diagnostics;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ class KAutoObject
+ {
+ protected KernelContext KernelContext;
+
+ private int _referenceCount;
+
+ public KAutoObject(KernelContext context)
+ {
+ KernelContext = context;
+
+ _referenceCount = 1;
+ }
+
+ public virtual Result SetName(string name)
+ {
+ if (!KernelContext.AutoObjectNames.TryAdd(name, this))
+ {
+ return KernelResult.InvalidState;
+ }
+
+ return Result.Success;
+ }
+
+ public static Result RemoveName(KernelContext context, string name)
+ {
+ if (!context.AutoObjectNames.TryRemove(name, out _))
+ {
+ return KernelResult.NotFound;
+ }
+
+ return Result.Success;
+ }
+
+ public static KAutoObject FindNamedObject(KernelContext context, string name)
+ {
+ if (context.AutoObjectNames.TryGetValue(name, out KAutoObject obj))
+ {
+ return obj;
+ }
+
+ return null;
+ }
+
+ public void IncrementReferenceCount()
+ {
+ int newRefCount = Interlocked.Increment(ref _referenceCount);
+
+ Debug.Assert(newRefCount >= 2);
+ }
+
+ public void DecrementReferenceCount()
+ {
+ int newRefCount = Interlocked.Decrement(ref _referenceCount);
+
+ Debug.Assert(newRefCount >= 0);
+
+ if (newRefCount == 0)
+ {
+ Destroy();
+ }
+ }
+
+ protected virtual void Destroy()
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs
new file mode 100644
index 00000000..b1a602f1
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs
@@ -0,0 +1,188 @@
+using Ryujinx.Common;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Horizon.Common;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ class KResourceLimit : KAutoObject
+ {
+ private const int DefaultTimeoutMs = 10000; // 10s
+
+ private readonly long[] _current;
+ private readonly long[] _limit;
+ private readonly long[] _current2;
+ private readonly long[] _peak;
+
+ private readonly object _lock;
+
+ private readonly LinkedList<KThread> _waitingThreads;
+
+ private int _waitingThreadsCount;
+
+ public KResourceLimit(KernelContext context) : base(context)
+ {
+ _current = new long[(int)LimitableResource.Count];
+ _limit = new long[(int)LimitableResource.Count];
+ _current2 = new long[(int)LimitableResource.Count];
+ _peak = new long[(int)LimitableResource.Count];
+
+ _lock = new object();
+
+ _waitingThreads = new LinkedList<KThread>();
+ }
+
+ 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(DefaultTimeoutMs));
+ }
+
+ 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 (_lock)
+ {
+ if (_current2[index] >= _limit[index])
+ {
+ return false;
+ }
+
+ long newCurrent = _current[index] + amount;
+
+ while (newCurrent > _limit[index] && _current2[index] + amount <= _limit[index])
+ {
+ _waitingThreadsCount++;
+
+ KConditionVariable.Wait(KernelContext, _waitingThreads, _lock, timeout);
+
+ _waitingThreadsCount--;
+
+ newCurrent = _current[index] + amount;
+
+ if (timeout >= 0 && PerformanceCounter.ElapsedMilliseconds > endTimePoint)
+ {
+ break;
+ }
+ }
+
+ if (newCurrent <= _limit[index])
+ {
+ _current[index] = newCurrent;
+ _current2[index] += amount;
+
+ if (_current[index] > _peak[index])
+ {
+ _peak[index] = _current[index];
+ }
+
+ 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);
+ }
+
+ public void Release(LimitableResource resource, long amount, long amount2)
+ {
+ int index = GetIndex(resource);
+
+ lock (_lock)
+ {
+ _current[index] -= amount;
+ _current2[index] -= amount2;
+
+ if (_waitingThreadsCount > 0)
+ {
+ KConditionVariable.NotifyAll(KernelContext, _waitingThreads);
+ }
+ }
+ }
+
+ public long GetRemainingValue(LimitableResource resource)
+ {
+ int index = GetIndex(resource);
+
+ lock (_lock)
+ {
+ return _limit[index] - _current[index];
+ }
+ }
+
+ public long GetCurrentValue(LimitableResource resource)
+ {
+ int index = GetIndex(resource);
+
+ lock (_lock)
+ {
+ return _current[index];
+ }
+ }
+
+ public long GetLimitValue(LimitableResource resource)
+ {
+ int index = GetIndex(resource);
+
+ lock (_lock)
+ {
+ return _limit[index];
+ }
+ }
+
+ public long GetPeakValue(LimitableResource resource)
+ {
+ int index = GetIndex(resource);
+
+ lock (_lock)
+ {
+ return _peak[index];
+ }
+ }
+
+ public Result SetLimitValue(LimitableResource resource, long limit)
+ {
+ int index = GetIndex(resource);
+
+ lock (_lock)
+ {
+ if (_current[index] <= limit)
+ {
+ _limit[index] = limit;
+ _peak[index] = _current[index];
+
+ return Result.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidState;
+ }
+ }
+ }
+
+ private static int GetIndex(LimitableResource resource)
+ {
+ return (int)resource;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs
new file mode 100644
index 00000000..ddc0069d
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs
@@ -0,0 +1,35 @@
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ class KSynchronizationObject : KAutoObject
+ {
+ public LinkedList<KThread> WaitingThreads { get; }
+
+ public KSynchronizationObject(KernelContext context) : base(context)
+ {
+ WaitingThreads = new LinkedList<KThread>();
+ }
+
+ public LinkedListNode<KThread> AddWaitingThread(KThread thread)
+ {
+ return WaitingThreads.AddLast(thread);
+ }
+
+ public void RemoveWaitingThread(LinkedListNode<KThread> node)
+ {
+ WaitingThreads.Remove(node);
+ }
+
+ public virtual void Signal()
+ {
+ KernelContext.Synchronization.SignalObject(this);
+ }
+
+ public virtual bool IsSignaled()
+ {
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs
new file mode 100644
index 00000000..8a727c30
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs
@@ -0,0 +1,78 @@
+using Ryujinx.HLE.HOS.Kernel.Memory;
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ static class KSystemControl
+ {
+ private const ulong KiB = 1024;
+ private const ulong MiB = 1024 * KiB;
+ private const ulong GiB = 1024 * MiB;
+
+ private const ulong PageSize = 4 * KiB;
+
+ private const ulong RequiredNonSecureSystemPoolSizeVi = 0x2238 * PageSize;
+ private const ulong RequiredNonSecureSystemPoolSizeNvservices = 0x710 * PageSize;
+ private const ulong RequiredNonSecureSystemPoolSizeOther = 0x80 * PageSize;
+
+ private const ulong RequiredNonSecureSystemPoolSize =
+ RequiredNonSecureSystemPoolSizeVi +
+ RequiredNonSecureSystemPoolSizeNvservices +
+ RequiredNonSecureSystemPoolSizeOther;
+
+ public static ulong GetApplicationPoolSize(MemoryArrange arrange)
+ {
+ return arrange switch
+ {
+ MemoryArrange.MemoryArrange4GiB or
+ MemoryArrange.MemoryArrange4GiBSystemDev or
+ MemoryArrange.MemoryArrange6GiBAppletDev => 3285 * MiB,
+ MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB,
+ MemoryArrange.MemoryArrange6GiB or
+ MemoryArrange.MemoryArrange8GiB => 4916 * MiB,
+ _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\".")
+ };
+ }
+
+ public static ulong GetAppletPoolSize(MemoryArrange arrange)
+ {
+ return arrange switch
+ {
+ MemoryArrange.MemoryArrange4GiB => 507 * MiB,
+ MemoryArrange.MemoryArrange4GiBAppletDev => 1554 * MiB,
+ MemoryArrange.MemoryArrange4GiBSystemDev => 448 * MiB,
+ MemoryArrange.MemoryArrange6GiB => 562 * MiB,
+ MemoryArrange.MemoryArrange6GiBAppletDev or
+ MemoryArrange.MemoryArrange8GiB => 2193 * MiB,
+ _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\".")
+ };
+ }
+
+ public static ulong GetMinimumNonSecureSystemPoolSize()
+ {
+ return RequiredNonSecureSystemPoolSize;
+ }
+
+ public static ulong GetDramEndAddress(MemorySize size)
+ {
+ return DramMemoryMap.DramBase + GetDramSize(size);
+ }
+
+ public static ulong GenerateRandom()
+ {
+ // TODO
+ return 0;
+ }
+
+ public static ulong GetDramSize(MemorySize size)
+ {
+ return size switch
+ {
+ MemorySize.MemorySize4GiB => 4 * GiB,
+ MemorySize.MemorySize6GiB => 6 * GiB,
+ MemorySize.MemorySize8GiB => 8 * GiB,
+ _ => throw new ArgumentException($"Invalid memory size \"{size}\".")
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
new file mode 100644
index 00000000..c0cd9ce9
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
@@ -0,0 +1,218 @@
+using Ryujinx.Common;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ class KTimeManager : IDisposable
+ {
+ public static readonly long DefaultTimeIncrementNanoseconds = ConvertGuestTicksToNanoseconds(2);
+
+ private class WaitingObject
+ {
+ public IKFutureSchedulerObject Object { get; }
+ public long TimePoint { get; }
+
+ public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
+ {
+ Object = schedulerObj;
+ TimePoint = timePoint;
+ }
+ }
+
+ private readonly KernelContext _context;
+ private readonly List<WaitingObject> _waitingObjects;
+ private AutoResetEvent _waitEvent;
+ private bool _keepRunning;
+ private long _enforceWakeupFromSpinWait;
+
+ private const long NanosecondsPerSecond = 1000000000L;
+ private const long NanosecondsPerMillisecond = 1000000L;
+
+ public KTimeManager(KernelContext context)
+ {
+ _context = context;
+ _waitingObjects = new List<WaitingObject>();
+ _keepRunning = true;
+
+ Thread work = new Thread(WaitAndCheckScheduledObjects)
+ {
+ Name = "HLE.TimeManager"
+ };
+
+ work.Start();
+ }
+
+ public void ScheduleFutureInvocation(IKFutureSchedulerObject schedulerObj, long timeout)
+ {
+ long startTime = PerformanceCounter.ElapsedTicks;
+ long timePoint = startTime + ConvertNanosecondsToHostTicks(timeout);
+
+ if (timePoint < startTime)
+ {
+ timePoint = long.MaxValue;
+ }
+
+ lock (_context.CriticalSection.Lock)
+ {
+ _waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
+
+ if (timeout < NanosecondsPerMillisecond)
+ {
+ Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1);
+ }
+ }
+
+ _waitEvent.Set();
+ }
+
+ public void UnscheduleFutureInvocation(IKFutureSchedulerObject schedulerObj)
+ {
+ lock (_context.CriticalSection.Lock)
+ {
+ for (int index = _waitingObjects.Count - 1; index >= 0; index--)
+ {
+ if (_waitingObjects[index].Object == schedulerObj)
+ {
+ _waitingObjects.RemoveAt(index);
+ }
+ }
+ }
+ }
+
+ private void WaitAndCheckScheduledObjects()
+ {
+ SpinWait spinWait = new SpinWait();
+ WaitingObject next;
+
+ using (_waitEvent = new AutoResetEvent(false))
+ {
+ while (_keepRunning)
+ {
+ lock (_context.CriticalSection.Lock)
+ {
+ Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 0);
+
+ next = GetNextWaitingObject();
+ }
+
+ if (next != null)
+ {
+ long timePoint = PerformanceCounter.ElapsedTicks;
+
+ if (next.TimePoint > timePoint)
+ {
+ long ms = Math.Min((next.TimePoint - timePoint) / PerformanceCounter.TicksPerMillisecond, int.MaxValue);
+
+ if (ms > 0)
+ {
+ _waitEvent.WaitOne((int)ms);
+ }
+ else
+ {
+ while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks < next.TimePoint)
+ {
+ // Our time is close - don't let SpinWait go off and potentially Thread.Sleep().
+ if (spinWait.NextSpinWillYield)
+ {
+ Thread.Yield();
+
+ spinWait.Reset();
+ }
+ else
+ {
+ spinWait.SpinOnce();
+ }
+ }
+
+ spinWait.Reset();
+ }
+ }
+
+ bool timeUp = PerformanceCounter.ElapsedTicks >= next.TimePoint;
+
+ if (timeUp)
+ {
+ lock (_context.CriticalSection.Lock)
+ {
+ if (_waitingObjects.Remove(next))
+ {
+ next.Object.TimeUp();
+ }
+ }
+ }
+ }
+ else
+ {
+ _waitEvent.WaitOne();
+ }
+ }
+ }
+ }
+
+ private WaitingObject GetNextWaitingObject()
+ {
+ WaitingObject selected = null;
+
+ long lowestTimePoint = long.MaxValue;
+
+ for (int index = _waitingObjects.Count - 1; index >= 0; index--)
+ {
+ WaitingObject current = _waitingObjects[index];
+
+ if (current.TimePoint <= lowestTimePoint)
+ {
+ selected = current;
+ lowestTimePoint = current.TimePoint;
+ }
+ }
+
+ return selected;
+ }
+
+ public static long ConvertNanosecondsToMilliseconds(long time)
+ {
+ time /= NanosecondsPerMillisecond;
+
+ if ((ulong)time > int.MaxValue)
+ {
+ return int.MaxValue;
+ }
+
+ return time;
+ }
+
+ public static long ConvertMillisecondsToNanoseconds(long time)
+ {
+ return time * NanosecondsPerMillisecond;
+ }
+
+ public static long ConvertNanosecondsToHostTicks(long ns)
+ {
+ long nsDiv = ns / NanosecondsPerSecond;
+ long nsMod = ns % NanosecondsPerSecond;
+ long tickDiv = PerformanceCounter.TicksPerSecond / NanosecondsPerSecond;
+ long tickMod = PerformanceCounter.TicksPerSecond % NanosecondsPerSecond;
+
+ long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / NanosecondsPerSecond;
+ return (nsDiv * tickDiv) * NanosecondsPerSecond + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
+ }
+
+ public static long ConvertGuestTicksToNanoseconds(long ticks)
+ {
+ return (long)Math.Ceiling(ticks * (1000000000.0 / 19200000.0));
+ }
+
+ public static long ConvertHostTicksToTicks(long time)
+ {
+ return (long)((time / (double)PerformanceCounter.TicksPerSecond) * 19200000.0);
+ }
+
+ public void Dispose()
+ {
+ _keepRunning = false;
+ _waitEvent?.Set();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
new file mode 100644
index 00000000..efa2a480
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
@@ -0,0 +1,89 @@
+using Ryujinx.HLE.HOS.Kernel.Memory;
+using Ryujinx.Horizon.Common;
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ static class KernelInit
+ {
+ private readonly struct MemoryRegion
+ {
+ public ulong Address { get; }
+ public ulong Size { get; }
+
+ public ulong EndAddress => Address + Size;
+
+ public MemoryRegion(ulong address, ulong size)
+ {
+ Address = address;
+ Size = size;
+ }
+ }
+
+ public static void InitializeResourceLimit(KResourceLimit resourceLimit, MemorySize size)
+ {
+ void EnsureSuccess(Result result)
+ {
+ if (result != Result.Success)
+ {
+ throw new InvalidOperationException($"Unexpected result \"{result}\".");
+ }
+ }
+
+ ulong ramSize = KSystemControl.GetDramSize(size);
+
+ EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Memory, (long)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(MemorySize size, MemoryArrange arrange)
+ {
+ ulong poolEnd = KSystemControl.GetDramEndAddress(size);
+ ulong applicationPoolSize = KSystemControl.GetApplicationPoolSize(arrange);
+ ulong appletPoolSize = KSystemControl.GetAppletPoolSize(arrange);
+
+ MemoryRegion servicePool;
+ MemoryRegion nvServicesPool;
+ MemoryRegion appletPool;
+ MemoryRegion applicationPool;
+
+ ulong nvServicesPoolSize = KSystemControl.GetMinimumNonSecureSystemPoolSize();
+
+ applicationPool = new MemoryRegion(poolEnd - applicationPoolSize, applicationPoolSize);
+
+ ulong nvServicesPoolEnd = applicationPool.Address - appletPoolSize;
+
+ nvServicesPool = new MemoryRegion(nvServicesPoolEnd - nvServicesPoolSize, nvServicesPoolSize);
+ appletPool = new MemoryRegion(nvServicesPoolEnd, appletPoolSize);
+
+ // 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 servicePoolSize = nvServicesPool.Address - DramMemoryMap.SlabHeapEnd;
+
+ servicePool = new MemoryRegion(DramMemoryMap.SlabHeapEnd, servicePoolSize);
+
+ return new KMemoryRegionManager[]
+ {
+ GetMemoryRegion(applicationPool),
+ GetMemoryRegion(appletPool),
+ GetMemoryRegion(servicePool),
+ GetMemoryRegion(nvServicesPool)
+ };
+ }
+
+ private static KMemoryRegionManager GetMemoryRegion(MemoryRegion region)
+ {
+ return new KMemoryRegionManager(region.Address, region.Size, region.EndAddress);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
new file mode 100644
index 00000000..cbc276c5
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
@@ -0,0 +1,73 @@
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ static class KernelTransfer
+ {
+ public static bool UserToKernel<T>(out T value, ulong address) where T : unmanaged
+ {
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
+
+ if (currentProcess.CpuMemory.IsRangeMapped(address, (ulong)Unsafe.SizeOf<T>()))
+ {
+ value = currentProcess.CpuMemory.Read<T>(address);
+
+ return true;
+ }
+
+ value = default;
+
+ return false;
+ }
+
+ public static bool UserToKernelArray<T>(ulong address, Span<T> values) where T : unmanaged
+ {
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
+
+ Span<byte> data = MemoryMarshal.Cast<T, byte>(values);
+
+ if (currentProcess.CpuMemory.IsRangeMapped(address, (ulong)data.Length))
+ {
+ currentProcess.CpuMemory.Read(address, data);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public static bool UserToKernelString(out string value, ulong address, uint size)
+ {
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
+
+ if (currentProcess.CpuMemory.IsRangeMapped(address, size))
+ {
+ value = MemoryHelper.ReadAsciiString(currentProcess.CpuMemory, address, size);
+
+ return true;
+ }
+
+ value = null;
+
+ return false;
+ }
+
+ public static bool KernelToUser<T>(ulong address, T value) where T: unmanaged
+ {
+ KProcess currentProcess = KernelStatic.GetCurrentProcess();
+
+ if (currentProcess.CpuMemory.IsRangeMapped(address, (ulong)Unsafe.SizeOf<T>()))
+ {
+ currentProcess.CpuMemory.Write(address, value);
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs
new file mode 100644
index 00000000..2e6a3e45
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ enum LimitableResource : byte
+ {
+ Memory = 0,
+ Thread = 1,
+ Event = 2,
+ TransferMemory = 3,
+ Session = 4,
+
+ Count = 5
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs
new file mode 100644
index 00000000..d2bcfd62
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ enum MemoryArrange : byte
+ {
+ MemoryArrange4GiB,
+ MemoryArrange4GiBAppletDev,
+ MemoryArrange4GiBSystemDev,
+ MemoryArrange6GiB,
+ MemoryArrange6GiBAppletDev,
+ MemoryArrange8GiB
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs
new file mode 100644
index 00000000..159385b6
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ enum MemorySize : byte
+ {
+ MemorySize4GiB = 0,
+ MemorySize6GiB = 1,
+ MemorySize8GiB = 2
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs
new file mode 100644
index 00000000..4c99f425
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs
@@ -0,0 +1,128 @@
+using System.Numerics;
+
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+ 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 - BitOperations.LeadingZeroCount((ulong)range);
+
+ // If Range is already power of 2, subtract one to use log2(Range) directly.
+ int rangeLog2 = nextRangeLog2 - (BitOperations.IsPow2(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;
+ }
+ }
+}