mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-07-31 16:07:11 +02:00
Move solution and projects to src
This commit is contained in:
parent
cd124bda58
commit
cee7121058
3466 changed files with 55 additions and 55 deletions
|
@ -0,0 +1,7 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
interface IKFutureSchedulerObject
|
||||
{
|
||||
void TimeUp();
|
||||
}
|
||||
}
|
73
src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
Normal file
73
src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
Normal file
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
188
src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs
Normal file
188
src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
35
src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs
Normal file
35
src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
78
src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs
Normal file
78
src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs
Normal file
|
@ -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}\".")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
218
src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
Normal file
218
src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
89
src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
Normal file
89
src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
73
src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
Normal file
73
src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
13
src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs
Normal file
13
src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
12
src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs
Normal file
12
src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
enum MemoryArrange : byte
|
||||
{
|
||||
MemoryArrange4GiB,
|
||||
MemoryArrange4GiBAppletDev,
|
||||
MemoryArrange4GiBSystemDev,
|
||||
MemoryArrange6GiB,
|
||||
MemoryArrange6GiBAppletDev,
|
||||
MemoryArrange8GiB
|
||||
}
|
||||
}
|
9
src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs
Normal file
9
src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
enum MemorySize : byte
|
||||
{
|
||||
MemorySize4GiB = 0,
|
||||
MemorySize6GiB = 1,
|
||||
MemorySize8GiB = 2
|
||||
}
|
||||
}
|
128
src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs
Normal file
128
src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue