Move solution and projects to src

This commit is contained in:
TSR Berry 2023-04-08 01:22:00 +02:00 committed by Mary
parent cd124bda58
commit cee7121058
3466 changed files with 55 additions and 55 deletions

View file

@ -0,0 +1,7 @@
namespace Ryujinx.HLE.HOS.Kernel.Common
{
interface IKFutureSchedulerObject
{
void TimeUp();
}
}

View 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()
{
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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}\".")
};
}
}
}

View 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();
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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
}
}

View file

@ -0,0 +1,12 @@
namespace Ryujinx.HLE.HOS.Kernel.Common
{
enum MemoryArrange : byte
{
MemoryArrange4GiB,
MemoryArrange4GiBAppletDev,
MemoryArrange4GiBSystemDev,
MemoryArrange6GiB,
MemoryArrange6GiBAppletDev,
MemoryArrange8GiB
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.HLE.HOS.Kernel.Common
{
enum MemorySize : byte
{
MemorySize4GiB = 0,
MemorySize6GiB = 1,
MemorySize8GiB = 2
}
}

View 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;
}
}
}