diff --git a/ChocolArm64/AOptimizations.cs b/ChocolArm64/AOptimizations.cs index 800cf363d..fbf26a491 100644 --- a/ChocolArm64/AOptimizations.cs +++ b/ChocolArm64/AOptimizations.cs @@ -2,8 +2,6 @@ using System.Runtime.Intrinsics.X86; public static class AOptimizations { - public static bool DisableMemoryChecks = false; - public static bool GenerateCallStack = true; private static bool UseAllSseIfAvailable = true; diff --git a/ChocolArm64/Exceptions/VmmAccessViolationException.cs b/ChocolArm64/Exceptions/VmmAccessViolationException.cs deleted file mode 100644 index a557502e5..000000000 --- a/ChocolArm64/Exceptions/VmmAccessViolationException.cs +++ /dev/null @@ -1,14 +0,0 @@ -using ChocolArm64.Memory; -using System; - -namespace ChocolArm64.Exceptions -{ - public class VmmAccessViolationException : Exception - { - private const string ExMsg = "Address 0x{0:x16} does not have \"{1}\" permission!"; - - public VmmAccessViolationException() { } - - public VmmAccessViolationException(long Position, AMemoryPerm Perm) : base(string.Format(ExMsg, Position, Perm)) { } - } -} \ No newline at end of file diff --git a/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs b/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs index c11384dae..4a03b65cb 100644 --- a/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs +++ b/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs @@ -2,12 +2,12 @@ using System; namespace ChocolArm64.Exceptions { - public class VmmOutOfMemoryException : Exception + public class VmmAccessException : Exception { - private const string ExMsg = "Failed to allocate {0} bytes of memory!"; + private const string ExMsg = "Memory region at 0x{0} with size 0x{1} is not contiguous!"; - public VmmOutOfMemoryException() { } + public VmmAccessException() { } - public VmmOutOfMemoryException(long Size) : base(string.Format(ExMsg, Size)) { } + public VmmAccessException(long Position, long Size) : base(string.Format(ExMsg, Position, Size)) { } } } \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs index df091bd51..b10551fe0 100644 --- a/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs @@ -45,46 +45,21 @@ namespace ChocolArm64.Instruction { switch (Size) { - case 0: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.ReadVector8Unchecked) - : nameof(AMemory.ReadVector8); break; - - case 1: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.ReadVector16Unchecked) - : nameof(AMemory.ReadVector16); break; - - case 2: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.ReadVector32Unchecked) - : nameof(AMemory.ReadVector32); break; - - case 3: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.ReadVector64Unchecked) - : nameof(AMemory.ReadVector64); break; - - case 4: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.ReadVector128Unchecked) - : nameof(AMemory.ReadVector128); break; + case 0: Name = nameof(AMemory.ReadVector8); break; + case 1: Name = nameof(AMemory.ReadVector16); break; + case 2: Name = nameof(AMemory.ReadVector32); break; + case 3: Name = nameof(AMemory.ReadVector64); break; + case 4: Name = nameof(AMemory.ReadVector128); break; } } else { switch (Size) { - case 0: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.ReadByteUnchecked) - : nameof(AMemory.ReadByte); break; - - case 1: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.ReadUInt16Unchecked) - : nameof(AMemory.ReadUInt16); break; - - case 2: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.ReadUInt32Unchecked) - : nameof(AMemory.ReadUInt32); break; - - case 3: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.ReadUInt64Unchecked) - : nameof(AMemory.ReadUInt64); break; + case 0: Name = nameof(AMemory.ReadByte); break; + case 1: Name = nameof(AMemory.ReadUInt16); break; + case 2: Name = nameof(AMemory.ReadUInt32); break; + case 3: Name = nameof(AMemory.ReadUInt64); break; } } @@ -132,46 +107,21 @@ namespace ChocolArm64.Instruction { switch (Size) { - case 0: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.WriteVector8Unchecked) - : nameof(AMemory.WriteVector8); break; - - case 1: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.WriteVector16Unchecked) - : nameof(AMemory.WriteVector16); break; - - case 2: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.WriteVector32Unchecked) - : nameof(AMemory.WriteVector32); break; - - case 3: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.WriteVector64Unchecked) - : nameof(AMemory.WriteVector64); break; - - case 4: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.WriteVector128Unchecked) - : nameof(AMemory.WriteVector128); break; + case 0: Name = nameof(AMemory.WriteVector8); break; + case 1: Name = nameof(AMemory.WriteVector16); break; + case 2: Name = nameof(AMemory.WriteVector32); break; + case 3: Name = nameof(AMemory.WriteVector64); break; + case 4: Name = nameof(AMemory.WriteVector128); break; } } else { switch (Size) { - case 0: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.WriteByteUnchecked) - : nameof(AMemory.WriteByte); break; - - case 1: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.WriteUInt16Unchecked) - : nameof(AMemory.WriteUInt16); break; - - case 2: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.WriteUInt32Unchecked) - : nameof(AMemory.WriteUInt32); break; - - case 3: Name = AOptimizations.DisableMemoryChecks - ? nameof(AMemory.WriteUInt64Unchecked) - : nameof(AMemory.WriteUInt64); break; + case 0: Name = nameof(AMemory.WriteByte); break; + case 1: Name = nameof(AMemory.WriteUInt16); break; + case 2: Name = nameof(AMemory.WriteUInt32); break; + case 3: Name = nameof(AMemory.WriteUInt64); break; } } diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs index e969cca5f..1b4ff6fb1 100644 --- a/ChocolArm64/Memory/AMemory.cs +++ b/ChocolArm64/Memory/AMemory.cs @@ -1,6 +1,7 @@ using ChocolArm64.Exceptions; using ChocolArm64.State; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -12,9 +13,22 @@ namespace ChocolArm64.Memory { public unsafe class AMemory : IAMemory, IDisposable { - private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1; + private const int PTLvl0Bits = 13; + private const int PTLvl1Bits = 14; + private const int PTPageBits = 12; - public AMemoryMgr Manager { get; private set; } + private const int PTLvl0Size = 1 << PTLvl0Bits; + private const int PTLvl1Size = 1 << PTLvl1Bits; + public const int PageSize = 1 << PTPageBits; + + private const int PTLvl0Mask = PTLvl0Size - 1; + private const int PTLvl1Mask = PTLvl1Size - 1; + public const int PageMask = PageSize - 1; + + private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; + private const int PTLvl1Bit = PTPageBits; + + private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1; private class ArmMonitor { @@ -29,32 +43,30 @@ namespace ChocolArm64.Memory private Dictionary Monitors; + private ConcurrentDictionary ObservedPages; + public IntPtr Ram { get; private set; } private byte* RamPtr; - private int HostPageSize; + private byte*** PageTable; - public AMemory() + public AMemory(IntPtr Ram) { - Manager = new AMemoryMgr(); - Monitors = new Dictionary(); - IntPtr Size = (IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize; + ObservedPages = new ConcurrentDictionary(); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Ram = AMemoryWin32.Allocate(Size); - - HostPageSize = AMemoryWin32.GetPageSize(Ram, Size); - } - else - { - Ram = Marshal.AllocHGlobal(Size); - } + this.Ram = Ram; RamPtr = (byte*)Ram; + + PageTable = (byte***)Marshal.AllocHGlobal(PTLvl0Size * IntPtr.Size); + + for (int L0 = 0; L0 < PTLvl0Size; L0++) + { + PageTable[L0] = null; + } } public void RemoveMonitor(AThreadState State) @@ -155,62 +167,6 @@ namespace ChocolArm64.Memory } } - public int GetHostPageSize() - { - return HostPageSize; - } - - public (bool[], long) IsRegionModified(long Position, long Size) - { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return (null, 0); - } - - long EndPos = Position + Size; - - if ((ulong)EndPos < (ulong)Position) - { - return (null, 0); - } - - if ((ulong)EndPos > AMemoryMgr.RamSize) - { - return (null, 0); - } - - IntPtr MemAddress = new IntPtr(RamPtr + Position); - IntPtr MemSize = new IntPtr(Size); - - int HostPageMask = HostPageSize - 1; - - Position &= ~HostPageMask; - - Size = EndPos - Position; - - IntPtr[] Addresses = new IntPtr[(Size + HostPageMask) / HostPageSize]; - - AMemoryWin32.IsRegionModified(MemAddress, MemSize, Addresses, out int Count); - - bool[] Modified = new bool[Addresses.Length]; - - for (int Index = 0; Index < Count; Index++) - { - long VA = Addresses[Index].ToInt64() - Ram.ToInt64(); - - Modified[(VA - Position) / HostPageSize] = true; - } - - return (Modified, Count); - } - - public IntPtr GetHostAddress(long Position, long Size) - { - EnsureRangeIsValid(Position, Size, AMemoryPerm.Read); - - return (IntPtr)(RamPtr + (ulong)Position); - } - public sbyte ReadSByte(long Position) { return (sbyte)ReadByte(Position); @@ -233,33 +189,22 @@ namespace ChocolArm64.Memory public byte ReadByte(long Position) { - EnsureAccessIsValid(Position, AMemoryPerm.Read); - - return ReadByteUnchecked(Position); + return *((byte*)Translate(Position)); } public ushort ReadUInt16(long Position) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); - EnsureAccessIsValid(Position + 1, AMemoryPerm.Read); - - return ReadUInt16Unchecked(Position); + return *((ushort*)Translate(Position)); } public uint ReadUInt32(long Position) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); - EnsureAccessIsValid(Position + 3, AMemoryPerm.Read); - - return ReadUInt32Unchecked(Position); + return *((uint*)Translate(Position)); } public ulong ReadUInt64(long Position) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); - EnsureAccessIsValid(Position + 7, AMemoryPerm.Read); - - return ReadUInt64Unchecked(Position); + return *((ulong*)Translate(Position)); } public Vector128 ReadVector8(long Position) @@ -274,6 +219,7 @@ namespace ChocolArm64.Memory } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector128 ReadVector16(long Position) { if (Sse2.IsSupported) @@ -286,14 +232,12 @@ namespace ChocolArm64.Memory } } + [MethodImpl(MethodImplOptions.NoInlining)] public Vector128 ReadVector32(long Position) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); - EnsureAccessIsValid(Position + 3, AMemoryPerm.Read); - if (Sse.IsSupported) { - return Sse.LoadScalarVector128((float*)(RamPtr + (uint)Position)); + return Sse.LoadScalarVector128((float*)Translate(Position)); } else { @@ -301,14 +245,12 @@ namespace ChocolArm64.Memory } } + [MethodImpl(MethodImplOptions.NoInlining)] public Vector128 ReadVector64(long Position) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); - EnsureAccessIsValid(Position + 7, AMemoryPerm.Read); - if (Sse2.IsSupported) { - return Sse.StaticCast(Sse2.LoadScalarVector128((double*)(RamPtr + (uint)Position))); + return Sse.StaticCast(Sse2.LoadScalarVector128((double*)Translate(Position))); } else { @@ -316,118 +258,12 @@ namespace ChocolArm64.Memory } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector128 ReadVector128(long Position) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); - EnsureAccessIsValid(Position + 15, AMemoryPerm.Read); - if (Sse.IsSupported) { - return Sse.LoadVector128((float*)(RamPtr + (uint)Position)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - public sbyte ReadSByteUnchecked(long Position) - { - return (sbyte)ReadByteUnchecked(Position); - } - - public short ReadInt16Unchecked(long Position) - { - return (short)ReadUInt16Unchecked(Position); - } - - public int ReadInt32Unchecked(long Position) - { - return (int)ReadUInt32Unchecked(Position); - } - - public long ReadInt64Unchecked(long Position) - { - return (long)ReadUInt64Unchecked(Position); - } - - public byte ReadByteUnchecked(long Position) - { - return *((byte*)(RamPtr + (uint)Position)); - } - - public ushort ReadUInt16Unchecked(long Position) - { - return *((ushort*)(RamPtr + (uint)Position)); - } - - public uint ReadUInt32Unchecked(long Position) - { - return *((uint*)(RamPtr + (uint)Position)); - } - - public ulong ReadUInt64Unchecked(long Position) - { - return *((ulong*)(RamPtr + (uint)Position)); - } - - public Vector128 ReadVector8Unchecked(long Position) - { - if (Sse2.IsSupported) - { - return Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ReadByte(Position))); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector128 ReadVector16Unchecked(long Position) - { - if (Sse2.IsSupported) - { - return Sse.StaticCast(Sse2.Insert(Sse2.SetZeroVector128(), ReadUInt16Unchecked(Position), 0)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public Vector128 ReadVector32Unchecked(long Position) - { - if (Sse.IsSupported) - { - return Sse.LoadScalarVector128((float*)(RamPtr + (uint)Position)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public Vector128 ReadVector64Unchecked(long Position) - { - if (Sse2.IsSupported) - { - return Sse.StaticCast(Sse2.LoadScalarVector128((double*)(RamPtr + (uint)Position))); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector128 ReadVector128Unchecked(long Position) - { - if (Sse.IsSupported) - { - return Sse.LoadVector128((float*)(RamPtr + (uint)Position)); + return Sse.LoadVector128((float*)Translate(Position)); } else { @@ -442,11 +278,11 @@ namespace ChocolArm64.Memory throw new ArgumentOutOfRangeException(nameof(Size)); } - EnsureRangeIsValid(Position, Size, AMemoryPerm.Read); + EnsureRangeIsValid(Position, Size); byte[] Data = new byte[Size]; - Marshal.Copy((IntPtr)(RamPtr + (uint)Position), Data, 0, (int)Size); + Marshal.Copy((IntPtr)Translate(Position), Data, 0, (int)Size); return Data; } @@ -473,35 +309,25 @@ namespace ChocolArm64.Memory public void WriteByte(long Position, byte Value) { - EnsureAccessIsValid(Position, AMemoryPerm.Write); - - WriteByteUnchecked(Position, Value); + *((byte*)TranslateWrite(Position)) = Value; } public void WriteUInt16(long Position, ushort Value) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); - EnsureAccessIsValid(Position + 1, AMemoryPerm.Write); - - WriteUInt16Unchecked(Position, Value); + *((ushort*)TranslateWrite(Position)) = Value; } public void WriteUInt32(long Position, uint Value) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); - EnsureAccessIsValid(Position + 3, AMemoryPerm.Write); - - WriteUInt32Unchecked(Position, Value); + *((uint*)TranslateWrite(Position)) = Value; } public void WriteUInt64(long Position, ulong Value) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); - EnsureAccessIsValid(Position + 7, AMemoryPerm.Write); - - WriteUInt64Unchecked(Position, Value); + *((ulong*)TranslateWrite(Position)) = Value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector8(long Position, Vector128 Value) { if (Sse41.IsSupported) @@ -518,6 +344,7 @@ namespace ChocolArm64.Memory } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector16(long Position, Vector128 Value) { if (Sse2.IsSupported) @@ -530,14 +357,12 @@ namespace ChocolArm64.Memory } } + [MethodImpl(MethodImplOptions.NoInlining)] public void WriteVector32(long Position, Vector128 Value) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); - EnsureAccessIsValid(Position + 3, AMemoryPerm.Write); - if (Sse.IsSupported) { - Sse.StoreScalar((float*)(RamPtr + (uint)Position), Value); + Sse.StoreScalar((float*)TranslateWrite(Position), Value); } else { @@ -545,14 +370,12 @@ namespace ChocolArm64.Memory } } + [MethodImpl(MethodImplOptions.NoInlining)] public void WriteVector64(long Position, Vector128 Value) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); - EnsureAccessIsValid(Position + 7, AMemoryPerm.Write); - if (Sse2.IsSupported) { - Sse2.StoreScalar((double*)(RamPtr + (uint)Position), Sse.StaticCast(Value)); + Sse2.StoreScalar((double*)TranslateWrite(Position), Sse.StaticCast(Value)); } else { @@ -560,123 +383,12 @@ namespace ChocolArm64.Memory } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector128(long Position, Vector128 Value) { - EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); - EnsureAccessIsValid(Position + 15, AMemoryPerm.Write); - if (Sse.IsSupported) { - Sse.Store((float*)(RamPtr + (uint)Position), Value); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - public void WriteSByteUnchecked(long Position, sbyte Value) - { - WriteByteUnchecked(Position, (byte)Value); - } - - public void WriteInt16Unchecked(long Position, short Value) - { - WriteUInt16Unchecked(Position, (ushort)Value); - } - - public void WriteInt32Unchecked(long Position, int Value) - { - WriteUInt32Unchecked(Position, (uint)Value); - } - - public void WriteInt64Unchecked(long Position, long Value) - { - WriteUInt64Unchecked(Position, (ulong)Value); - } - - public void WriteByteUnchecked(long Position, byte Value) - { - *((byte*)(RamPtr + (uint)Position)) = Value; - } - - public void WriteUInt16Unchecked(long Position, ushort Value) - { - *((ushort*)(RamPtr + (uint)Position)) = Value; - } - - public void WriteUInt32Unchecked(long Position, uint Value) - { - *((uint*)(RamPtr + (uint)Position)) = Value; - } - - public void WriteUInt64Unchecked(long Position, ulong Value) - { - *((ulong*)(RamPtr + (uint)Position)) = Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVector8Unchecked(long Position, Vector128 Value) - { - if (Sse41.IsSupported) - { - WriteByteUnchecked(Position, Sse41.Extract(Sse.StaticCast(Value), 0)); - } - else if (Sse2.IsSupported) - { - WriteByteUnchecked(Position, (byte)Sse2.Extract(Sse.StaticCast(Value), 0)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVector16Unchecked(long Position, Vector128 Value) - { - if (Sse2.IsSupported) - { - WriteUInt16Unchecked(Position, Sse2.Extract(Sse.StaticCast(Value), 0)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public void WriteVector32Unchecked(long Position, Vector128 Value) - { - if (Sse.IsSupported) - { - Sse.StoreScalar((float*)(RamPtr + (uint)Position), Value); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public void WriteVector64Unchecked(long Position, Vector128 Value) - { - if (Sse2.IsSupported) - { - Sse2.StoreScalar((double*)(RamPtr + (uint)Position), Sse.StaticCast(Value)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVector128Unchecked(long Position, Vector128 Value) - { - if (Sse.IsSupported) - { - Sse.Store((float*)(RamPtr + (uint)Position), Value); + Sse.Store((float*)TranslateWrite(Position), Value); } else { @@ -686,36 +398,285 @@ namespace ChocolArm64.Memory public void WriteBytes(long Position, byte[] Data) { - EnsureRangeIsValid(Position, (uint)Data.Length, AMemoryPerm.Write); + EnsureRangeIsValid(Position, (uint)Data.Length); - Marshal.Copy(Data, 0, (IntPtr)(RamPtr + (uint)Position), Data.Length); + Marshal.Copy(Data, 0, (IntPtr)TranslateWrite(Position), Data.Length); } - private void EnsureRangeIsValid(long Position, long Size, AMemoryPerm Perm) + public void Map(long VA, long PA, long Size) + { + SetPTEntries(VA, RamPtr + PA, Size); + } + + public void Unmap(long Position, long Size) + { + SetPTEntries(Position, null, Size); + + StopObservingRegion(Position, Size); + } + + public bool IsMapped(long Position) + { + if (!(IsValidPosition(Position))) + { + return false; + } + + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + if (PageTable[L0] == null) + { + return false; + } + + return PageTable[L0][L1] != null || ObservedPages.ContainsKey(Position >> PTPageBits); + } + + public long GetPhysicalAddress(long VirtualAddress) + { + byte* Ptr = Translate(VirtualAddress); + + return (long)(Ptr - RamPtr); + } + + internal byte* Translate(long Position) + { + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + long Old = Position; + + byte** Lvl1 = PageTable[L0]; + + if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0) + { + goto Unmapped; + } + + if (Lvl1 == null) + { + goto Unmapped; + } + + Position &= PageMask; + + byte* Ptr = Lvl1[L1]; + + if (Ptr == null) + { + goto Unmapped; + } + + return Ptr + Position; + +Unmapped: + return HandleNullPte(Old); + } + + private byte* HandleNullPte(long Position) + { + long Key = Position >> PTPageBits; + + if (ObservedPages.TryGetValue(Key, out IntPtr Ptr)) + { + return (byte*)Ptr + (Position & PageMask); + } + + throw new VmmPageFaultException(Position); + } + + internal byte* TranslateWrite(long Position) + { + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + long Old = Position; + + byte** Lvl1 = PageTable[L0]; + + if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0) + { + goto Unmapped; + } + + if (Lvl1 == null) + { + goto Unmapped; + } + + Position &= PageMask; + + byte* Ptr = Lvl1[L1]; + + if (Ptr == null) + { + goto Unmapped; + } + + return Ptr + Position; + +Unmapped: + return HandleNullPteWrite(Old); + } + + private byte* HandleNullPteWrite(long Position) + { + long Key = Position >> PTPageBits; + + if (ObservedPages.TryGetValue(Key, out IntPtr Ptr)) + { + SetPTEntry(Position, (byte*)Ptr); + + return (byte*)Ptr + (Position & PageMask); + } + + throw new VmmPageFaultException(Position); + } + + private void SetPTEntries(long VA, byte* Ptr, long Size) + { + long EndPosition = (VA + Size + PageMask) & ~PageMask; + + while ((ulong)VA < (ulong)EndPosition) + { + SetPTEntry(VA, Ptr); + + VA += PageSize; + + if (Ptr != null) + { + Ptr += PageSize; + } + } + } + + private void SetPTEntry(long Position, byte* Ptr) + { + if (!IsValidPosition(Position)) + { + throw new ArgumentOutOfRangeException(nameof(Position)); + } + + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + if (PageTable[L0] == null) + { + byte** Lvl1 = (byte**)Marshal.AllocHGlobal(PTLvl1Size * IntPtr.Size); + + for (int ZL1 = 0; ZL1 < PTLvl1Size; ZL1++) + { + Lvl1[ZL1] = null; + } + + Thread.MemoryBarrier(); + + PageTable[L0] = Lvl1; + } + + PageTable[L0][L1] = Ptr; + } + + public (bool[], int) IsRegionModified(long Position, long Size) + { + long EndPosition = (Position + Size + PageMask) & ~PageMask; + + Position &= ~PageMask; + + Size = EndPosition - Position; + + bool[] Modified = new bool[Size >> PTPageBits]; + + int Count = 0; + + lock (ObservedPages) + { + for (int Page = 0; Page < Modified.Length; Page++) + { + byte* Ptr = Translate(Position); + + if (ObservedPages.TryAdd(Position >> PTPageBits, (IntPtr)Ptr)) + { + Modified[Page] = true; + + Count++; + } + else + { + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + byte** Lvl1 = PageTable[L0]; + + if (Lvl1 != null) + { + if (Modified[Page] = Lvl1[L1] != null) + { + Count++; + } + } + } + + SetPTEntry(Position, null); + + Position += PageSize; + } + } + + return (Modified, Count); + } + + public void StopObservingRegion(long Position, long Size) + { + long EndPosition = (Position + Size + PageMask) & ~PageMask; + + while (Position < EndPosition) + { + lock (ObservedPages) + { + if (ObservedPages.TryRemove(Position >> PTPageBits, out IntPtr Ptr)) + { + SetPTEntry(Position, (byte*)Ptr); + } + } + + Position += PageSize; + } + } + + public IntPtr GetHostAddress(long Position, long Size) + { + EnsureRangeIsValid(Position, Size); + + return (IntPtr)Translate(Position); + } + + internal void EnsureRangeIsValid(long Position, long Size) { long EndPos = Position + Size; - Position &= ~AMemoryMgr.PageMask; + Position &= ~PageMask; + + long ExpectedPA = GetPhysicalAddress(Position); while ((ulong)Position < (ulong)EndPos) { - EnsureAccessIsValid(Position, Perm); + long PA = GetPhysicalAddress(Position); - Position += AMemoryMgr.PageSize; + if (PA != ExpectedPA) + { + throw new VmmAccessException(Position, Size); + } + + Position += PageSize; + ExpectedPA += PageSize; } } - private void EnsureAccessIsValid(long Position, AMemoryPerm Perm) + public bool IsValidPosition(long Position) { - if (!Manager.IsMapped(Position)) - { - throw new VmmPageFaultException(Position); - } - - if (!Manager.HasPermission(Position, Perm)) - { - throw new VmmAccessViolationException(Position, Perm); - } + return Position >> (PTLvl0Bits + PTLvl1Bits + PTPageBits) == 0; } public void Dispose() @@ -725,19 +686,24 @@ namespace ChocolArm64.Memory protected virtual void Dispose(bool disposing) { - if (Ram != IntPtr.Zero) + if (PageTable == null) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return; + } + + for (int L0 = 0; L0 < PTLvl0Size; L0++) + { + if (PageTable[L0] != null) { - AMemoryWin32.Free(Ram); - } - else - { - Marshal.FreeHGlobal(Ram); + Marshal.FreeHGlobal((IntPtr)PageTable[L0]); } - Ram = IntPtr.Zero; + PageTable[L0] = null; } + + Marshal.FreeHGlobal((IntPtr)PageTable); + + PageTable = null; } } } \ No newline at end of file diff --git a/ChocolArm64/Memory/AMemoryHelper.cs b/ChocolArm64/Memory/AMemoryHelper.cs index 0a23a2f89..ea8778343 100644 --- a/ChocolArm64/Memory/AMemoryHelper.cs +++ b/ChocolArm64/Memory/AMemoryHelper.cs @@ -26,12 +26,9 @@ namespace ChocolArm64.Memory { long Size = Marshal.SizeOf(); - if ((ulong)(Position + Size) > AMemoryMgr.AddrSize) - { - throw new ArgumentOutOfRangeException(nameof(Position)); - } + Memory.EnsureRangeIsValid(Position, Size); - IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position); + IntPtr Ptr = (IntPtr)Memory.Translate(Position); return Marshal.PtrToStructure(Ptr); } @@ -40,12 +37,9 @@ namespace ChocolArm64.Memory { long Size = Marshal.SizeOf(); - if ((ulong)(Position + Size) > AMemoryMgr.AddrSize) - { - throw new ArgumentOutOfRangeException(nameof(Position)); - } + Memory.EnsureRangeIsValid(Position, Size); - IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position); + IntPtr Ptr = (IntPtr)Memory.TranslateWrite(Position); Marshal.StructureToPtr(Value, Ptr, false); } @@ -69,15 +63,5 @@ namespace ChocolArm64.Memory return Encoding.ASCII.GetString(MS.ToArray()); } } - - public static long PageRoundUp(long Value) - { - return (Value + AMemoryMgr.PageMask) & ~AMemoryMgr.PageMask; - } - - public static long PageRoundDown(long Value) - { - return Value & ~AMemoryMgr.PageMask; - } } } \ No newline at end of file diff --git a/ChocolArm64/Memory/AMemoryMapInfo.cs b/ChocolArm64/Memory/AMemoryMapInfo.cs deleted file mode 100644 index 02dd3055c..000000000 --- a/ChocolArm64/Memory/AMemoryMapInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace ChocolArm64.Memory -{ - public class AMemoryMapInfo - { - public long Position { get; private set; } - public long Size { get; private set; } - public int Type { get; private set; } - public int Attr { get; private set; } - - public AMemoryPerm Perm { get; private set; } - - public AMemoryMapInfo(long Position, long Size, int Type, int Attr, AMemoryPerm Perm) - { - this.Position = Position; - this.Size = Size; - this.Type = Type; - this.Attr = Attr; - this.Perm = Perm; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Memory/AMemoryMgr.cs b/ChocolArm64/Memory/AMemoryMgr.cs deleted file mode 100644 index 8a165b079..000000000 --- a/ChocolArm64/Memory/AMemoryMgr.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; - -namespace ChocolArm64.Memory -{ - public class AMemoryMgr - { - public const long RamSize = 4L * 1024 * 1024 * 1024; - public const long AddrSize = RamSize; - - private const int PTLvl0Bits = 10; - private const int PTLvl1Bits = 10; - private const int PTPageBits = 12; - - private const int PTLvl0Size = 1 << PTLvl0Bits; - private const int PTLvl1Size = 1 << PTLvl1Bits; - public const int PageSize = 1 << PTPageBits; - - private const int PTLvl0Mask = PTLvl0Size - 1; - private const int PTLvl1Mask = PTLvl1Size - 1; - public const int PageMask = PageSize - 1; - - private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; - private const int PTLvl1Bit = PTPageBits; - - private enum PTMap - { - Unmapped, - Mapped - } - - private struct PTEntry - { - public PTMap Map; - public AMemoryPerm Perm; - - public int Type; - public int Attr; - - public PTEntry(PTMap Map, AMemoryPerm Perm, int Type, int Attr) - { - this.Map = Map; - this.Perm = Perm; - this.Type = Type; - this.Attr = Attr; - } - } - - private PTEntry[][] PageTable; - - public AMemoryMgr() - { - PageTable = new PTEntry[PTLvl0Size][]; - } - - public void Map(long Position, long Size, int Type, AMemoryPerm Perm) - { - SetPTEntry(Position, Size, new PTEntry(PTMap.Mapped, Perm, Type, 0)); - } - - public void Unmap(long Position, long Size) - { - SetPTEntry(Position, Size, new PTEntry(PTMap.Unmapped, 0, 0, 0)); - } - - public void Unmap(long Position, long Size, int Type) - { - SetPTEntry(Position, Size, Type, new PTEntry(PTMap.Unmapped, 0, 0, 0)); - } - - public void Reprotect(long Position, long Size, AMemoryPerm Perm) - { - Position = AMemoryHelper.PageRoundDown(Position); - - Size = AMemoryHelper.PageRoundUp(Size); - - long PagesCount = Size / PageSize; - - while (PagesCount-- > 0) - { - PTEntry Entry = GetPTEntry(Position); - - Entry.Perm = Perm; - - SetPTEntry(Position, Entry); - - Position += PageSize; - } - } - - public AMemoryMapInfo GetMapInfo(long Position) - { - if (!IsValidPosition(Position)) - { - return null; - } - - Position = AMemoryHelper.PageRoundDown(Position); - - PTEntry BaseEntry = GetPTEntry(Position); - - bool IsSameSegment(long Pos) - { - if (!IsValidPosition(Pos)) - { - return false; - } - - PTEntry Entry = GetPTEntry(Pos); - - return Entry.Map == BaseEntry.Map && - Entry.Perm == BaseEntry.Perm && - Entry.Type == BaseEntry.Type && - Entry.Attr == BaseEntry.Attr; - } - - long Start = Position; - long End = Position + PageSize; - - while (Start > 0 && IsSameSegment(Start - PageSize)) - { - Start -= PageSize; - } - - while (End < AddrSize && IsSameSegment(End)) - { - End += PageSize; - } - - long Size = End - Start; - - return new AMemoryMapInfo( - Start, - Size, - BaseEntry.Type, - BaseEntry.Attr, - BaseEntry.Perm); - } - - public void ClearAttrBit(long Position, long Size, int Bit) - { - while (Size > 0) - { - PTEntry Entry = GetPTEntry(Position); - - Entry.Attr &= ~(1 << Bit); - - SetPTEntry(Position, Entry); - - Position += PageSize; - Size -= PageSize; - } - } - - public void SetAttrBit(long Position, long Size, int Bit) - { - while (Size > 0) - { - PTEntry Entry = GetPTEntry(Position); - - Entry.Attr |= (1 << Bit); - - SetPTEntry(Position, Entry); - - Position += PageSize; - Size -= PageSize; - } - } - - public bool HasPermission(long Position, AMemoryPerm Perm) - { - return GetPTEntry(Position).Perm.HasFlag(Perm); - } - - public bool IsValidPosition(long Position) - { - if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) - { - return false; - } - - return true; - } - - public bool IsMapped(long Position) - { - if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) - { - return false; - } - - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - if (PageTable[L0] == null) - { - return false; - } - - return PageTable[L0][L1].Map != PTMap.Unmapped; - } - - private PTEntry GetPTEntry(long Position) - { - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - if (PageTable[L0] == null) - { - return default(PTEntry); - } - - return PageTable[L0][L1]; - } - - private void SetPTEntry(long Position, long Size, PTEntry Entry) - { - while (Size > 0) - { - SetPTEntry(Position, Entry); - - Position += PageSize; - Size -= PageSize; - } - } - - private void SetPTEntry(long Position, long Size, int Type, PTEntry Entry) - { - while (Size > 0) - { - if (GetPTEntry(Position).Type == Type) - { - SetPTEntry(Position, Entry); - } - - Position += PageSize; - Size -= PageSize; - } - } - - private void SetPTEntry(long Position, PTEntry Entry) - { - if (!IsValidPosition(Position)) - { - throw new ArgumentOutOfRangeException(nameof(Position)); - } - - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - if (PageTable[L0] == null) - { - PageTable[L0] = new PTEntry[PTLvl1Size]; - } - - PageTable[L0][L1] = Entry; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Memory/AMemoryPerm.cs b/ChocolArm64/Memory/AMemoryPerm.cs deleted file mode 100644 index b425eb94b..000000000 --- a/ChocolArm64/Memory/AMemoryPerm.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace ChocolArm64.Memory -{ - [Flags] - public enum AMemoryPerm - { - None = 0, - Read = 1 << 0, - Write = 1 << 1, - Execute = 1 << 2, - RW = Read | Write, - RX = Read | Execute - } -} \ No newline at end of file diff --git a/ChocolArm64/Memory/AMemoryWin32.cs b/ChocolArm64/Memory/AMemoryWin32.cs deleted file mode 100644 index 387ca32c2..000000000 --- a/ChocolArm64/Memory/AMemoryWin32.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace ChocolArm64.Memory -{ - static class AMemoryWin32 - { - private const int MEM_COMMIT = 0x00001000; - private const int MEM_RESERVE = 0x00002000; - private const int MEM_WRITE_WATCH = 0x00200000; - - private const int PAGE_READWRITE = 0x04; - - private const int MEM_RELEASE = 0x8000; - - private const int WRITE_WATCH_FLAG_RESET = 1; - - [DllImport("kernel32.dll")] - private static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, int flAllocationType, int flProtect); - - [DllImport("kernel32.dll")] - private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, int dwFreeType); - - [DllImport("kernel32.dll")] - private unsafe static extern int GetWriteWatch( - int dwFlags, - IntPtr lpBaseAddress, - IntPtr dwRegionSize, - IntPtr[] lpAddresses, - long* lpdwCount, - long* lpdwGranularity); - - public static IntPtr Allocate(IntPtr Size) - { - const int Flags = MEM_COMMIT | MEM_RESERVE | MEM_WRITE_WATCH; - - IntPtr Address = VirtualAlloc(IntPtr.Zero, Size, Flags, PAGE_READWRITE); - - if (Address == IntPtr.Zero) - { - throw new InvalidOperationException(); - } - - return Address; - } - - public static void Free(IntPtr Address) - { - VirtualFree(Address, IntPtr.Zero, MEM_RELEASE); - } - - public unsafe static int GetPageSize(IntPtr Address, IntPtr Size) - { - IntPtr[] Addresses = new IntPtr[1]; - - long Count = Addresses.Length; - - long Granularity; - - GetWriteWatch( - 0, - Address, - Size, - Addresses, - &Count, - &Granularity); - - return (int)Granularity; - } - - public unsafe static void IsRegionModified( - IntPtr Address, - IntPtr Size, - IntPtr[] Addresses, - out int AddrCount) - { - long Count = Addresses.Length; - - long Granularity; - - GetWriteWatch( - WRITE_WATCH_FLAG_RESET, - Address, - Size, - Addresses, - &Count, - &Granularity); - - AddrCount = (int)Count; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalShader.cs b/Ryujinx.Graphics/Gal/IGalShader.cs index a9bd13811..5174c0393 100644 --- a/Ryujinx.Graphics/Gal/IGalShader.cs +++ b/Ryujinx.Graphics/Gal/IGalShader.cs @@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Gal void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type); + IEnumerable GetConstBufferUsage(long Key); IEnumerable GetTextureUsage(long Key); void EnsureTextureBinding(string UniformName, int Value); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs index 54f984cd6..c9e7f6c3a 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs @@ -279,7 +279,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { if (Stage != null) { - foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage) + foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage) { long Key = New.ConstBufferKeys[(int)Stage.Type][DeclInfo.Cbuf]; diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index 4792bc5e8..9c7b8668f 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -72,8 +72,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL return new OGLShaderStage( Type, Program.Code, - Program.Textures, - Program.Uniforms); + Program.Uniforms, + Program.Textures); + } + + public IEnumerable GetConstBufferUsage(long Key) + { + if (Stages.TryGetValue(Key, out OGLShaderStage Stage)) + { + return Stage.ConstBufferUsage; + } + + return Enumerable.Empty(); } public IEnumerable GetTextureUsage(long Key) @@ -224,7 +234,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { if (Stage != null) { - foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage) + foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage) { int BlockIndex = GL.GetUniformBlockIndex(ProgramHandle, DeclInfo.Name); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs index 731994cec..c4e6a881f 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs @@ -1,6 +1,7 @@ using OpenTK.Graphics.OpenGL; using System; using System.Collections.Generic; +using System.Linq; namespace Ryujinx.Graphics.Gal.OpenGL { @@ -23,19 +24,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL public string Code { get; private set; } - public IEnumerable TextureUsage { get; private set; } - public IEnumerable UniformUsage { get; private set; } + public IEnumerable ConstBufferUsage { get; private set; } + public IEnumerable TextureUsage { get; private set; } public OGLShaderStage( - GalShaderType Type, - string Code, - IEnumerable TextureUsage, - IEnumerable UniformUsage) + GalShaderType Type, + string Code, + IEnumerable ConstBufferUsage, + IEnumerable TextureUsage) { - this.Type = Type; - this.Code = Code; - this.TextureUsage = TextureUsage; - this.UniformUsage = UniformUsage; + this.Type = Type; + this.Code = Code; + this.ConstBufferUsage = ConstBufferUsage; + this.TextureUsage = TextureUsage; } public void Compile() diff --git a/Ryujinx.HLE/Font/SharedFontManager.cs b/Ryujinx.HLE/Font/SharedFontManager.cs deleted file mode 100644 index fce270de8..000000000 --- a/Ryujinx.HLE/Font/SharedFontManager.cs +++ /dev/null @@ -1,177 +0,0 @@ -using ChocolArm64.Exceptions; -using ChocolArm64.Memory; -using Ryujinx.HLE.Logging; -using Ryujinx.HLE.OsHle; -using Ryujinx.HLE.OsHle.Handles; -using Ryujinx.HLE.Resource; -using System; -using System.Collections.Generic; -using System.IO; - - -namespace Ryujinx.HLE.Font -{ - public class SharedFontManager - { - private const uint SharedMemorySize = 0x1100000; - private Logger Log; - - private string FontsPath; - - private object ShMemLock; - - private (AMemory, long, long)[] ShMemPositions; - - private Dictionary FontData; - - private uint[] LoadedFonts; - - public SharedFontManager(Logger Log, string SystemPath) - { - this.Log = Log; - this.FontsPath = Path.Combine(SystemPath, "fonts"); - - ShMemLock = new object(); - - ShMemPositions = new(AMemory, long, long)[0]; - - FontData = new Dictionary() - { - { SharedFontType.JapanUsEurope, GetData("FontStandard") }, - { SharedFontType.SimplifiedChinese, GetData("FontChineseSimplified") }, - { SharedFontType.SimplifiedChineseEx, GetData("FontExtendedChineseSimplified") }, - { SharedFontType.TraditionalChinese, GetData("FontChineseTraditional") }, - { SharedFontType.Korean, GetData("FontKorean") }, - { SharedFontType.NintendoEx, GetData("FontNintendoExtended") } - }; - - int FontMemoryUsage = 0; - foreach (byte[] data in FontData.Values) - { - FontMemoryUsage += data.Length; - FontMemoryUsage += 0x8; - } - - if (FontMemoryUsage > SharedMemorySize) - { - throw new InvalidSystemResourceException($"The sum of all fonts size exceed the shared memory size. Please make sure that the fonts don't exceed {SharedMemorySize} bytes in total. (actual size: {FontMemoryUsage} bytes)"); - } - - LoadedFonts = new uint[FontData.Count]; - } - - public byte[] GetData(string FontName) - { - string FontFilePath = Path.Combine(FontsPath, $"{FontName}.ttf"); - if (File.Exists(FontFilePath)) - { - return File.ReadAllBytes(FontFilePath); - } - else - { - throw new InvalidSystemResourceException($"Font \"{FontName}.ttf\" not found. Please provide it in \"{FontsPath}\"."); - } - } - - public void MapFont(SharedFontType FontType, AMemory Memory, long Position) - { - uint SharedMemoryAddressOffset = GetSharedMemoryAddressOffset(FontType); - // TODO: find what are the 8 bytes before the font - Memory.WriteUInt64(Position + SharedMemoryAddressOffset - 8, 0); - Memory.WriteBytes(Position + SharedMemoryAddressOffset, FontData[FontType]); - } - - public void PropagateNewMapFont(SharedFontType Type) - { - lock (ShMemLock) - { - foreach ((AMemory Memory, long Position, long Size) in ShMemPositions) - { - AMemoryMapInfo MemoryInfo = Memory.Manager.GetMapInfo(Position); - - if (MemoryInfo == null) - { - throw new VmmPageFaultException(Position); - } - - // The memory is read only, we need to changes that to add the new font - AMemoryPerm originalPerms = MemoryInfo.Perm; - Memory.Manager.Reprotect(Position, Size, AMemoryPerm.RW); - MapFont(Type, Memory, Position); - Memory.Manager.Reprotect(Position, Size, originalPerms); - } - } - } - - internal void ShMemMap(object sender, EventArgs e) - { - HSharedMem SharedMem = (HSharedMem)sender; - - lock (ShMemLock) - { - ShMemPositions = SharedMem.GetVirtualPositions(); - - (AMemory Memory, long Position, long Size) = ShMemPositions[ShMemPositions.Length - 1]; - - for (int Type = 0; Type < LoadedFonts.Length; Type++) - { - if (LoadedFonts[(int)Type] == 1) - { - MapFont((SharedFontType)Type, Memory, Position); - } - } - } - } - - internal void ShMemUnmap(object sender, EventArgs e) - { - HSharedMem SharedMem = (HSharedMem)sender; - - lock (ShMemLock) - { - ShMemPositions = SharedMem.GetVirtualPositions(); - } - } - - public void Load(SharedFontType FontType) - { - if (LoadedFonts[(int)FontType] == 0) - { - PropagateNewMapFont(FontType); - } - - LoadedFonts[(int)FontType] = 1; - } - - public uint GetLoadState(SharedFontType FontType) - { - if (LoadedFonts[(int)FontType] != 1) - { - // Some games don't request a load, so we need to load it here. - Load(FontType); - return 0; - } - return LoadedFonts[(int)FontType]; - } - - public uint GetFontSize(SharedFontType FontType) - { - return (uint)FontData[FontType].Length; - } - - public uint GetSharedMemoryAddressOffset(SharedFontType FontType) - { - uint Pos = 0x8; - - for (SharedFontType Type = SharedFontType.JapanUsEurope; Type < FontType; Type++) - { - Pos += GetFontSize(Type); - Pos += 0x8; - } - - return Pos; - } - - public int Count => FontData.Count; - } -} diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs index cb1514b5a..6f6012445 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs @@ -109,7 +109,7 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Shader.BindProgram(); UploadTextures(Vmm, State, Keys); - UploadConstBuffers(Vmm, State); + UploadConstBuffers(Vmm, State, Keys); UploadVertexArrays(Vmm, State); DispatchRender(Vmm, State); @@ -426,6 +426,12 @@ namespace Ryujinx.HLE.Gpu.Engines Key = Vmm.GetPhysicalAddress(Key); + if (Key == -1) + { + //FIXME: Should'nt ignore invalid addresses. + return; + } + if (IsFrameBufferPosition(Key)) { //This texture is a frame buffer texture, @@ -465,24 +471,29 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Texture.SetSampler(Sampler); } - private void UploadConstBuffers(NvGpuVmm Vmm, GalPipelineState State) + private void UploadConstBuffers(NvGpuVmm Vmm, GalPipelineState State, long[] Keys) { - for (int Stage = 0; Stage < State.ConstBufferKeys.Length; Stage++) + for (int Stage = 0; Stage < Keys.Length; Stage++) { - for (int Index = 0; Index < State.ConstBufferKeys[Stage].Length; Index++) + foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetConstBufferUsage(Keys[Stage])) { - ConstBuffer Cb = ConstBuffers[Stage][Index]; + ConstBuffer Cb = ConstBuffers[Stage][DeclInfo.Cbuf]; - long Key = Cb.Position; - - if (Cb.Enabled && QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer)) + if (!Cb.Enabled) { - IntPtr Source = Vmm.GetHostAddress(Key, Cb.Size); + continue; + } + + long Key = Vmm.GetPhysicalAddress(Cb.Position); + + if (QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer)) + { + IntPtr Source = Vmm.GetHostAddress(Cb.Position, Cb.Size); Gpu.Renderer.Buffer.SetData(Key, Cb.Size, Source); } - State.ConstBufferKeys[Stage][Index] = Key; + State.ConstBufferKeys[Stage][DeclInfo.Cbuf] = Key; } } } @@ -668,11 +679,13 @@ namespace Ryujinx.HLE.Gpu.Engines long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); + long CbKey = Vmm.GetPhysicalAddress(Position); + int Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); - if (!Gpu.Renderer.Buffer.IsCached(Position, Size)) + if (!Gpu.Renderer.Buffer.IsCached(CbKey, Size)) { - Gpu.Renderer.Buffer.Create(Position, Size); + Gpu.Renderer.Buffer.Create(CbKey, Size); } ConstBuffer Cb = ConstBuffers[Stage][Index]; diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs index 7b23e49fa..e7e180646 100644 --- a/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs +++ b/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs @@ -1,7 +1,6 @@ using ChocolArm64.Memory; using Ryujinx.Graphics.Gal; using System; -using System.Collections.Concurrent; namespace Ryujinx.HLE.Gpu.Memory { @@ -26,18 +25,6 @@ namespace Ryujinx.HLE.Gpu.Memory public AMemory Memory { get; private set; } - private struct MappedMemory - { - public long Size; - - public MappedMemory(long Size) - { - this.Size = Size; - } - } - - private ConcurrentDictionary Maps; - private NvGpuVmmCache Cache; private const long PteUnmapped = -1; @@ -49,8 +36,6 @@ namespace Ryujinx.HLE.Gpu.Memory { this.Memory = Memory; - Maps = new ConcurrentDictionary(); - Cache = new NvGpuVmmCache(); PageTable = new long[PTLvl0Size][]; @@ -60,14 +45,6 @@ namespace Ryujinx.HLE.Gpu.Memory { lock (PageTable) { - for (long Offset = 0; Offset < Size; Offset += PageSize) - { - if (GetPte(VA + Offset) != PteReserved) - { - return Map(PA, Size); - } - } - for (long Offset = 0; Offset < Size; Offset += PageSize) { SetPte(VA + Offset, PA + Offset); @@ -85,10 +62,6 @@ namespace Ryujinx.HLE.Gpu.Memory if (VA != -1) { - MappedMemory Map = new MappedMemory(Size); - - Maps.AddOrUpdate(VA, Map, (Key, Old) => Map); - for (long Offset = 0; Offset < Size; Offset += PageSize) { SetPte(VA + Offset, PA + Offset); @@ -99,19 +72,7 @@ namespace Ryujinx.HLE.Gpu.Memory } } - public bool Unmap(long VA) - { - if (Maps.TryRemove(VA, out MappedMemory Map)) - { - Free(VA, Map.Size); - - return true; - } - - return false; - } - - public long Reserve(long VA, long Size, long Align) + public long ReserveFixed(long VA, long Size) { lock (PageTable) { @@ -119,7 +80,7 @@ namespace Ryujinx.HLE.Gpu.Memory { if (IsPageInUse(VA + Offset)) { - return Reserve(Size, Align); + return -1; } } @@ -163,7 +124,9 @@ namespace Ryujinx.HLE.Gpu.Memory private long GetFreePosition(long Size, long Align = 1) { - long Position = 0; + //Note: Address 0 is not considered valid by the driver, + //when 0 is returned it's considered a mapping error. + long Position = PageSize; long FreeSize = 0; if (Align < 1) diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs index 3c2560443..b3f253b3e 100644 --- a/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs +++ b/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs @@ -1,4 +1,5 @@ using ChocolArm64.Memory; +using Ryujinx.HLE.Memory; using System; using System.Collections.Generic; @@ -129,14 +130,11 @@ namespace Ryujinx.HLE.Gpu.Memory { (bool[] Modified, long ModifiedCount) = Memory.IsRegionModified(PA, Size); - if (Modified == null) - { - return true; - } + PA = Memory.GetPhysicalAddress(PA); ClearCachedPagesIfNeeded(); - long PageSize = Memory.GetHostPageSize(); + long PageSize = AMemory.PageSize; EnsureResidencyInitialized(PageSize); @@ -159,9 +157,9 @@ namespace Ryujinx.HLE.Gpu.Memory while (PA < PAEnd) { - long Key = PA & ~Mask; + long Key = PA & ~AMemory.PageMask; - long PAPgEnd = Math.Min((PA + PageSize) & ~Mask, PAEnd); + long PAPgEnd = Math.Min((PA + AMemory.PageSize) & ~AMemory.PageMask, PAEnd); bool IsCached = Cache.TryGetValue(Key, out CachedPage Cp); @@ -228,7 +226,7 @@ namespace Ryujinx.HLE.Gpu.Memory { if (Residency == null) { - Residency = new HashSet[AMemoryMgr.RamSize / PageSize]; + Residency = new HashSet[DeviceMemory.RamSize / PageSize]; for (int i = 0; i < Residency.Length; i++) { diff --git a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs index f8cd67659..0c6103af8 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs @@ -72,7 +72,7 @@ namespace Ryujinx.HLE.Gpu.Texture { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - byte Pixel = CpuMem.ReadByteUnchecked(Position + Offset); + byte Pixel = CpuMem.ReadByte(Position + Offset); *(BuffPtr + OutOffs) = Pixel; @@ -105,7 +105,7 @@ namespace Ryujinx.HLE.Gpu.Texture { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - uint Pixel = (uint)CpuMem.ReadInt16Unchecked(Position + Offset); + uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset); Pixel = (Pixel & 0x001f) << 11 | (Pixel & 0x03e0) << 1 | @@ -143,7 +143,7 @@ namespace Ryujinx.HLE.Gpu.Texture { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - uint Pixel = (uint)CpuMem.ReadInt16Unchecked(Position + Offset); + uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset); Pixel = (Pixel & 0x001f) << 11 | (Pixel & 0x07e0) | @@ -180,7 +180,7 @@ namespace Ryujinx.HLE.Gpu.Texture { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - short Pixel = CpuMem.ReadInt16Unchecked(Position + Offset); + short Pixel = CpuMem.ReadInt16(Position + Offset); *(short*)(BuffPtr + OutOffs) = Pixel; @@ -213,7 +213,7 @@ namespace Ryujinx.HLE.Gpu.Texture { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - int Pixel = CpuMem.ReadInt32Unchecked(Position + Offset); + int Pixel = CpuMem.ReadInt32(Position + Offset); *(int*)(BuffPtr + OutOffs) = Pixel; @@ -246,7 +246,7 @@ namespace Ryujinx.HLE.Gpu.Texture { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - long Pixel = CpuMem.ReadInt64Unchecked(Position + Offset); + long Pixel = CpuMem.ReadInt64(Position + Offset); *(long*)(BuffPtr + OutOffs) = Pixel; @@ -279,8 +279,8 @@ namespace Ryujinx.HLE.Gpu.Texture { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - long PxLow = CpuMem.ReadInt64Unchecked(Position + Offset + 0); - long PxHigh = CpuMem.ReadInt64Unchecked(Position + Offset + 8); + long PxLow = CpuMem.ReadInt64(Position + Offset + 0); + long PxHigh = CpuMem.ReadInt64(Position + Offset + 8); *(long*)(BuffPtr + OutOffs + 0) = PxLow; *(long*)(BuffPtr + OutOffs + 8) = PxHigh; @@ -314,7 +314,7 @@ namespace Ryujinx.HLE.Gpu.Texture { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - long Tile = CpuMem.ReadInt64Unchecked(Position + Offset); + long Tile = CpuMem.ReadInt64(Position + Offset); *(long*)(BuffPtr + OutOffs) = Tile; @@ -347,8 +347,8 @@ namespace Ryujinx.HLE.Gpu.Texture { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - long Tile0 = CpuMem.ReadInt64Unchecked(Position + Offset + 0); - long Tile1 = CpuMem.ReadInt64Unchecked(Position + Offset + 8); + long Tile0 = CpuMem.ReadInt64(Position + Offset + 0); + long Tile1 = CpuMem.ReadInt64(Position + Offset + 8); *(long*)(BuffPtr + OutOffs + 0) = Tile0; *(long*)(BuffPtr + OutOffs + 8) = Tile1; diff --git a/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs b/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs index a87d4545b..113dc6f61 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.Gpu.Texture int Pixel = *(int*)(BuffPtr + InOffs); - CpuMem.WriteInt32Unchecked(Position + Offset, Pixel); + CpuMem.WriteInt32(Position + Offset, Pixel); InOffs += 4; } diff --git a/Ryujinx.HLE/Hid/Hid.cs b/Ryujinx.HLE/Hid/Hid.cs index 43c040bb5..492eb30a8 100644 --- a/Ryujinx.HLE/Hid/Hid.cs +++ b/Ryujinx.HLE/Hid/Hid.cs @@ -1,7 +1,4 @@ -using ChocolArm64.Memory; -using Ryujinx.HLE.Logging; -using Ryujinx.HLE.OsHle; -using Ryujinx.HLE.OsHle.Handles; +using Ryujinx.HLE.OsHle; using System; namespace Ryujinx.HLE.Input @@ -63,57 +60,18 @@ namespace Ryujinx.HLE.Input private const int HidEntryCount = 17; - private Logger Log; + private Switch Device; - private object ShMemLock; + private long HidPosition; - private (AMemory, long, long)[] ShMemPositions; - - public Hid(Logger Log) + public Hid(Switch Device, long HidPosition) { - this.Log = Log; + this.Device = Device; + this.HidPosition = HidPosition; - ShMemLock = new object(); + Device.Memory.FillWithZeros(HidPosition, Horizon.HidSize); - ShMemPositions = new (AMemory, long, long)[0]; - } - - internal void ShMemMap(object sender, EventArgs e) - { - HSharedMem SharedMem = (HSharedMem)sender; - - lock (ShMemLock) - { - ShMemPositions = SharedMem.GetVirtualPositions(); - - (AMemory Memory, long Position, long Size) = ShMemPositions[ShMemPositions.Length - 1]; - - for (long Offset = 0; Offset < Horizon.HidSize; Offset += 8) - { - Memory.WriteInt64Unchecked(Position + Offset, 0); - } - - Log.PrintInfo(LogClass.Hid, $"HID shared memory successfully mapped to 0x{Position:x16}!"); - - Init(Memory, Position); - } - } - - internal void ShMemUnmap(object sender, EventArgs e) - { - HSharedMem SharedMem = (HSharedMem)sender; - - lock (ShMemLock) - { - ShMemPositions = SharedMem.GetVirtualPositions(); - } - } - - private void Init(AMemory Memory, long Position) - { InitializeJoyconPair( - Memory, - Position, JoyConColor.Body_Neon_Red, JoyConColor.Buttons_Neon_Red, JoyConColor.Body_Neon_Blue, @@ -121,14 +79,12 @@ namespace Ryujinx.HLE.Input } private void InitializeJoyconPair( - AMemory Memory, - long Position, JoyConColor LeftColorBody, JoyConColor LeftColorButtons, JoyConColor RightColorBody, JoyConColor RightColorButtons) { - long BaseControllerOffset = Position + HidControllersOffset + 8 * HidControllerSize; + long BaseControllerOffset = HidPosition + HidControllersOffset + 8 * HidControllerSize; HidControllerType Type = HidControllerType.ControllerType_Handheld; @@ -142,20 +98,20 @@ namespace Ryujinx.HLE.Input HidControllerColorDesc SplitColorDesc = 0; - Memory.WriteInt32Unchecked(BaseControllerOffset + 0x0, (int)Type); + Device.Memory.WriteInt32(BaseControllerOffset + 0x0, (int)Type); - Memory.WriteInt32Unchecked(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); + Device.Memory.WriteInt32(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); - Memory.WriteInt32Unchecked(BaseControllerOffset + 0x8, (int)SingleColorDesc); - Memory.WriteInt32Unchecked(BaseControllerOffset + 0xc, (int)SingleColorBody); - Memory.WriteInt32Unchecked(BaseControllerOffset + 0x10, (int)SingleColorButtons); - Memory.WriteInt32Unchecked(BaseControllerOffset + 0x14, (int)SplitColorDesc); + Device.Memory.WriteInt32(BaseControllerOffset + 0x8, (int)SingleColorDesc); + Device.Memory.WriteInt32(BaseControllerOffset + 0xc, (int)SingleColorBody); + Device.Memory.WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons); + Device.Memory.WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc); - Memory.WriteInt32Unchecked(BaseControllerOffset + 0x18, (int)LeftColorBody); - Memory.WriteInt32Unchecked(BaseControllerOffset + 0x1c, (int)LeftColorButtons); + Device.Memory.WriteInt32(BaseControllerOffset + 0x18, (int)LeftColorBody); + Device.Memory.WriteInt32(BaseControllerOffset + 0x1c, (int)LeftColorButtons); - Memory.WriteInt32Unchecked(BaseControllerOffset + 0x20, (int)RightColorBody); - Memory.WriteInt32Unchecked(BaseControllerOffset + 0x24, (int)RightColorButtons); + Device.Memory.WriteInt32(BaseControllerOffset + 0x20, (int)RightColorBody); + Device.Memory.WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons); } public void SetJoyconButton( @@ -165,107 +121,95 @@ namespace Ryujinx.HLE.Input HidJoystickPosition LeftStick, HidJoystickPosition RightStick) { - lock (ShMemLock) - { - foreach ((AMemory Memory, long Position, long Size) in ShMemPositions) - { - long ControllerOffset = Position + HidControllersOffset; + long ControllerOffset = HidPosition + HidControllersOffset; - ControllerOffset += (int)ControllerId * HidControllerSize; + ControllerOffset += (int)ControllerId * HidControllerSize; - ControllerOffset += HidControllerHeaderSize; + ControllerOffset += HidControllerHeaderSize; - ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; + ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; - long LastEntry = Memory.ReadInt64Unchecked(ControllerOffset + 0x10); + long LastEntry = Device.Memory.ReadInt64(ControllerOffset + 0x10); - long CurrEntry = (LastEntry + 1) % HidEntryCount; + long CurrEntry = (LastEntry + 1) % HidEntryCount; - long Timestamp = GetTimestamp(); + long Timestamp = GetTimestamp(); - Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp); - Memory.WriteInt64Unchecked(ControllerOffset + 0x8, HidEntryCount); - Memory.WriteInt64Unchecked(ControllerOffset + 0x10, CurrEntry); - Memory.WriteInt64Unchecked(ControllerOffset + 0x18, HidEntryCount - 1); + Device.Memory.WriteInt64(ControllerOffset + 0x0, Timestamp); + Device.Memory.WriteInt64(ControllerOffset + 0x8, HidEntryCount); + Device.Memory.WriteInt64(ControllerOffset + 0x10, CurrEntry); + Device.Memory.WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1); - ControllerOffset += HidControllersLayoutHeaderSize; + ControllerOffset += HidControllersLayoutHeaderSize; - long LastEntryOffset = ControllerOffset + LastEntry * HidControllersInputEntrySize; + long LastEntryOffset = ControllerOffset + LastEntry * HidControllersInputEntrySize; - ControllerOffset += CurrEntry * HidControllersInputEntrySize; + ControllerOffset += CurrEntry * HidControllersInputEntrySize; - long SampleCounter = Memory.ReadInt64Unchecked(LastEntryOffset) + 1; + long SampleCounter = Device.Memory.ReadInt64(LastEntryOffset) + 1; - Memory.WriteInt64Unchecked(ControllerOffset + 0x0, SampleCounter); - Memory.WriteInt64Unchecked(ControllerOffset + 0x8, SampleCounter); + Device.Memory.WriteInt64(ControllerOffset + 0x0, SampleCounter); + Device.Memory.WriteInt64(ControllerOffset + 0x8, SampleCounter); - Memory.WriteInt64Unchecked(ControllerOffset + 0x10, (uint)Buttons); + Device.Memory.WriteInt64(ControllerOffset + 0x10, (uint)Buttons); - Memory.WriteInt32Unchecked(ControllerOffset + 0x18, LeftStick.DX); - Memory.WriteInt32Unchecked(ControllerOffset + 0x1c, LeftStick.DY); + Device.Memory.WriteInt32(ControllerOffset + 0x18, LeftStick.DX); + Device.Memory.WriteInt32(ControllerOffset + 0x1c, LeftStick.DY); - Memory.WriteInt32Unchecked(ControllerOffset + 0x20, RightStick.DX); - Memory.WriteInt32Unchecked(ControllerOffset + 0x24, RightStick.DY); + Device.Memory.WriteInt32(ControllerOffset + 0x20, RightStick.DX); + Device.Memory.WriteInt32(ControllerOffset + 0x24, RightStick.DY); - Memory.WriteInt64Unchecked(ControllerOffset + 0x28, - (uint)HidControllerConnState.Controller_State_Connected | - (uint)HidControllerConnState.Controller_State_Wired); - } - } + Device.Memory.WriteInt64(ControllerOffset + 0x28, + (uint)HidControllerConnState.Controller_State_Connected | + (uint)HidControllerConnState.Controller_State_Wired); } public void SetTouchPoints(params HidTouchPoint[] Points) { - lock (ShMemLock) + long TouchScreenOffset = HidPosition + HidTouchScreenOffset; + + long LastEntry = Device.Memory.ReadInt64(TouchScreenOffset + 0x10); + + long CurrEntry = (LastEntry + 1) % HidEntryCount; + + long Timestamp = GetTimestamp(); + + Device.Memory.WriteInt64(TouchScreenOffset + 0x0, Timestamp); + Device.Memory.WriteInt64(TouchScreenOffset + 0x8, HidEntryCount); + Device.Memory.WriteInt64(TouchScreenOffset + 0x10, CurrEntry); + Device.Memory.WriteInt64(TouchScreenOffset + 0x18, HidEntryCount - 1); + Device.Memory.WriteInt64(TouchScreenOffset + 0x20, Timestamp); + + long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize; + + long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize; + + long SampleCounter = Device.Memory.ReadInt64(LastEntryOffset) + 1; + + TouchEntryOffset += CurrEntry * HidTouchEntrySize; + + Device.Memory.WriteInt64(TouchEntryOffset + 0x0, SampleCounter); + Device.Memory.WriteInt64(TouchEntryOffset + 0x8, Points.Length); + + TouchEntryOffset += HidTouchEntryHeaderSize; + + const int Padding = 0; + + int Index = 0; + + foreach (HidTouchPoint Point in Points) { - foreach ((AMemory Memory, long Position, long Size) in ShMemPositions) - { - long TouchScreenOffset = Position + HidTouchScreenOffset; + Device.Memory.WriteInt64(TouchEntryOffset + 0x0, Timestamp); + Device.Memory.WriteInt32(TouchEntryOffset + 0x8, Padding); + Device.Memory.WriteInt32(TouchEntryOffset + 0xc, Index++); + Device.Memory.WriteInt32(TouchEntryOffset + 0x10, Point.X); + Device.Memory.WriteInt32(TouchEntryOffset + 0x14, Point.Y); + Device.Memory.WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX); + Device.Memory.WriteInt32(TouchEntryOffset + 0x1c, Point.DiameterY); + Device.Memory.WriteInt32(TouchEntryOffset + 0x20, Point.Angle); + Device.Memory.WriteInt32(TouchEntryOffset + 0x24, Padding); - long LastEntry = Memory.ReadInt64Unchecked(TouchScreenOffset + 0x10); - - long CurrEntry = (LastEntry + 1) % HidEntryCount; - - long Timestamp = GetTimestamp(); - - Memory.WriteInt64Unchecked(TouchScreenOffset + 0x0, Timestamp); - Memory.WriteInt64Unchecked(TouchScreenOffset + 0x8, HidEntryCount); - Memory.WriteInt64Unchecked(TouchScreenOffset + 0x10, CurrEntry); - Memory.WriteInt64Unchecked(TouchScreenOffset + 0x18, HidEntryCount - 1); - Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp); - - long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize; - - long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize; - - long SampleCounter = Memory.ReadInt64Unchecked(LastEntryOffset) + 1; - - TouchEntryOffset += CurrEntry * HidTouchEntrySize; - - Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, SampleCounter); - Memory.WriteInt64Unchecked(TouchEntryOffset + 0x8, Points.Length); - - TouchEntryOffset += HidTouchEntryHeaderSize; - - const int Padding = 0; - - int Index = 0; - - foreach (HidTouchPoint Point in Points) - { - Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, Timestamp); - Memory.WriteInt32Unchecked(TouchEntryOffset + 0x8, Padding); - Memory.WriteInt32Unchecked(TouchEntryOffset + 0xc, Index++); - Memory.WriteInt32Unchecked(TouchEntryOffset + 0x10, Point.X); - Memory.WriteInt32Unchecked(TouchEntryOffset + 0x14, Point.Y); - Memory.WriteInt32Unchecked(TouchEntryOffset + 0x18, Point.DiameterX); - Memory.WriteInt32Unchecked(TouchEntryOffset + 0x1c, Point.DiameterY); - Memory.WriteInt32Unchecked(TouchEntryOffset + 0x20, Point.Angle); - Memory.WriteInt32Unchecked(TouchEntryOffset + 0x24, Padding); - - TouchEntryOffset += HidTouchEntryTouchSize; - } - } + TouchEntryOffset += HidTouchEntryTouchSize; } } diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs index 84f5ff390..ba5f8d7ed 100644 --- a/Ryujinx.HLE/Loaders/Executable.cs +++ b/Ryujinx.HLE/Loaders/Executable.cs @@ -1,6 +1,8 @@ using ChocolArm64.Memory; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.OsHle; +using Ryujinx.HLE.OsHle.Handles; +using Ryujinx.HLE.OsHle.Utilities; using System.Collections.Generic; using System.IO; @@ -18,12 +20,14 @@ namespace Ryujinx.HLE.Loaders public string FilePath { get; private set; } - private AMemory Memory; - public long ImageBase { get; private set; } public long ImageEnd { get; private set; } - public Executable(IExecutable Exe, AMemory Memory, long ImageBase) + private AMemory Memory; + + private KMemoryManager MemoryManager; + + public Executable(IExecutable Exe, KMemoryManager MemoryManager, AMemory Memory, long ImageBase) { Dynamic = new List(); @@ -36,23 +40,34 @@ namespace Ryujinx.HLE.Loaders Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, "")); } - this.Memory = Memory; - this.ImageBase = ImageBase; - this.ImageEnd = ImageBase; + this.Memory = Memory; + this.MemoryManager = MemoryManager; + this.ImageBase = ImageBase; + this.ImageEnd = ImageBase; - WriteData(ImageBase + Exe.TextOffset, Exe.Text, MemoryType.CodeStatic, AMemoryPerm.RX); - WriteData(ImageBase + Exe.ROOffset, Exe.RO, MemoryType.CodeMutable, AMemoryPerm.Read); - WriteData(ImageBase + Exe.DataOffset, Exe.Data, MemoryType.CodeMutable, AMemoryPerm.RW); + long TextPosition = ImageBase + (uint)Exe.TextOffset; + long ROPosition = ImageBase + (uint)Exe.ROOffset; + long DataPosition = ImageBase + (uint)Exe.DataOffset; + + long TextSize = (uint)IntUtils.AlignUp(Exe.Text.Length, KMemoryManager.PageSize); + long ROSize = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize); + long DataSize = (uint)IntUtils.AlignUp(Exe.Data.Length, KMemoryManager.PageSize); + + long DataAndBssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize) + DataSize; + + ImageEnd = DataPosition + DataAndBssSize; + + MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize); + + MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); + MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite); + + Memory.WriteBytes(TextPosition, Exe.Text); + Memory.WriteBytes(ROPosition, Exe.RO); + Memory.WriteBytes(DataPosition, Exe.Data); if (Exe.Mod0Offset == 0) { - int BssOffset = Exe.DataOffset + Exe.Data.Length; - int BssSize = Exe.BssSize; - - MapBss(ImageBase + BssOffset, BssSize); - - ImageEnd = ImageBase + BssOffset + BssSize; - return; } @@ -66,10 +81,6 @@ namespace Ryujinx.HLE.Loaders long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset; long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset; - MapBss(BssStartOffset, BssEndOffset - BssStartOffset); - - ImageEnd = BssEndOffset; - while (true) { long TagVal = Memory.ReadInt64(DynamicOffset + 0); @@ -102,24 +113,6 @@ namespace Ryujinx.HLE.Loaders } } - private void WriteData( - long Position, - byte[] Data, - MemoryType Type, - AMemoryPerm Perm) - { - Memory.Manager.Map(Position, Data.Length, (int)Type, AMemoryPerm.Write); - - Memory.WriteBytes(Position, Data); - - Memory.Manager.Reprotect(Position, Data.Length, Perm); - } - - private void MapBss(long Position, long Size) - { - Memory.Manager.Map(Position, Size, (int)MemoryType.Normal, AMemoryPerm.RW); - } - private ElfRel GetRelocation(long Position) { long Offset = Memory.ReadInt64(Position + 0); diff --git a/Ryujinx.HLE/Memory/ArenaAllocator.cs b/Ryujinx.HLE/Memory/ArenaAllocator.cs new file mode 100644 index 000000000..5e15f46a5 --- /dev/null +++ b/Ryujinx.HLE/Memory/ArenaAllocator.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; + +namespace Ryujinx.HLE.Memory +{ + class ArenaAllocator + { + private class Region + { + public long Position { get; set; } + public long Size { get; set; } + + public Region(long Position, long Size) + { + this.Position = Position; + this.Size = Size; + } + } + + private LinkedList FreeRegions; + + public long TotalAvailableSize { get; private set; } + public long TotalUsedSize { get; private set; } + + public ArenaAllocator(long ArenaSize) + { + TotalAvailableSize = ArenaSize; + + FreeRegions = new LinkedList(); + + FreeRegions.AddFirst(new Region(0, ArenaSize)); + } + + public bool TryAllocate(long Size, out long Position) + { + LinkedListNode Node = FreeRegions.First; + + while (Node != null) + { + Region Rg = Node.Value; + + if ((ulong)Rg.Size >= (ulong)Size) + { + Position = Rg.Position; + + Rg.Position += Size; + Rg.Size -= Size; + + TotalUsedSize += Size; + + return true; + } + + Node = Node.Next; + } + + Position = 0; + + return false; + } + + public void Free(long Position, long Size) + { + long End = Position + Size; + + Region NewRg = new Region(Position, Size); + + LinkedListNode Node = FreeRegions.First; + LinkedListNode PrevSz = Node; + + while (Node != null) + { + LinkedListNode NextNode = Node.Next; + + Region Rg = Node.Value; + + long RgEnd = Rg.Position + Rg.Size; + + if (Rg.Position == End) + { + NewRg.Size += Rg.Size; + + FreeRegions.Remove(Node); + } + else if (RgEnd == Position) + { + NewRg.Position = Rg.Position; + NewRg.Size += Rg.Size; + + FreeRegions.Remove(Node); + } + else if ((ulong)Rg.Size < (ulong)NewRg.Size && + (ulong)Rg.Size > (ulong)PrevSz.Value.Size) + { + PrevSz = Node; + } + + Node = NextNode; + } + + if ((ulong)PrevSz.Value.Size < (ulong)Size) + { + FreeRegions.AddAfter(PrevSz, NewRg); + } + else + { + FreeRegions.AddFirst(NewRg); + } + + TotalUsedSize -= Size; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Memory/DeviceMemory.cs b/Ryujinx.HLE/Memory/DeviceMemory.cs new file mode 100644 index 000000000..3c5f2e5f7 --- /dev/null +++ b/Ryujinx.HLE/Memory/DeviceMemory.cs @@ -0,0 +1,130 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.Memory +{ + class DeviceMemory : IDisposable + { + public const long RamSize = 4L * 1024 * 1024 * 1024; + + public ArenaAllocator Allocator { get; private set; } + + public IntPtr RamPointer { get; private set; } + + private unsafe byte* RamPtr; + + public unsafe DeviceMemory() + { + Allocator = new ArenaAllocator(RamSize); + + RamPointer = Marshal.AllocHGlobal(new IntPtr(RamSize)); + + RamPtr = (byte*)RamPointer; + } + + public sbyte ReadSByte(long Position) + { + return (sbyte)ReadByte(Position); + } + + public short ReadInt16(long Position) + { + return (short)ReadUInt16(Position); + } + + public int ReadInt32(long Position) + { + return (int)ReadUInt32(Position); + } + + public long ReadInt64(long Position) + { + return (long)ReadUInt64(Position); + } + + public unsafe byte ReadByte(long Position) + { + return *((byte*)(RamPtr + Position)); + } + + public unsafe ushort ReadUInt16(long Position) + { + return *((ushort*)(RamPtr + Position)); + } + + public unsafe uint ReadUInt32(long Position) + { + return *((uint*)(RamPtr + Position)); + } + + public unsafe ulong ReadUInt64(long Position) + { + return *((ulong*)(RamPtr + Position)); + } + + public void WriteSByte(long Position, sbyte Value) + { + WriteByte(Position, (byte)Value); + } + + public void WriteInt16(long Position, short Value) + { + WriteUInt16(Position, (ushort)Value); + } + + public void WriteInt32(long Position, int Value) + { + WriteUInt32(Position, (uint)Value); + } + + public void WriteInt64(long Position, long Value) + { + WriteUInt64(Position, (ulong)Value); + } + + public unsafe void WriteByte(long Position, byte Value) + { + *((byte*)(RamPtr + Position)) = Value; + } + + public unsafe void WriteUInt16(long Position, ushort Value) + { + *((ushort*)(RamPtr + Position)) = Value; + } + + public unsafe void WriteUInt32(long Position, uint Value) + { + *((uint*)(RamPtr + Position)) = Value; + } + + public unsafe void WriteUInt64(long Position, ulong Value) + { + *((ulong*)(RamPtr + Position)) = Value; + } + + public void FillWithZeros(long Position, int Size) + { + int Size8 = Size & ~(8 - 1); + + for (int Offs = 0; Offs < Size8; Offs += 8) + { + WriteInt64(Position + Offs, 0); + } + + for (int Offs = Size8; Offs < (Size - Size8); Offs++) + { + WriteByte(Position + Offs, 0); + } + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + Marshal.FreeHGlobal(RamPointer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Font/SharedFontManager.cs b/Ryujinx.HLE/OsHle/Font/SharedFontManager.cs new file mode 100644 index 000000000..12b6973e0 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Font/SharedFontManager.cs @@ -0,0 +1,122 @@ +using Ryujinx.HLE.Memory; +using Ryujinx.HLE.OsHle.Utilities; +using Ryujinx.HLE.Resource; +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.OsHle.Font +{ + class SharedFontManager + { + private DeviceMemory Memory; + + private long PhysicalAddress; + + private string FontsPath; + + private struct FontInfo + { + public int Offset; + public int Size; + + public FontInfo(int Offset, int Size) + { + this.Offset = Offset; + this.Size = Size; + } + } + + private Dictionary FontData; + + public SharedFontManager(Switch Device, long PhysicalAddress) + { + this.PhysicalAddress = PhysicalAddress; + + Memory = Device.Memory; + + FontsPath = Path.Combine(Device.VFs.GetSystemPath(), "fonts"); + } + + public void EnsureInitialized() + { + if (FontData == null) + { + Memory.FillWithZeros(PhysicalAddress, Horizon.FontSize); + + uint FontOffset = 0; + + FontInfo CreateFont(string Name) + { + string FontFilePath = Path.Combine(FontsPath, Name + ".ttf"); + + if (File.Exists(FontFilePath)) + { + byte[] Data = File.ReadAllBytes(FontFilePath); + + FontInfo Info = new FontInfo((int)FontOffset, Data.Length); + + WriteMagicAndSize(PhysicalAddress + FontOffset, Data.Length); + + FontOffset += 8; + + uint Start = FontOffset; + + for (; FontOffset - Start < Data.Length; FontOffset++) + { + Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]); + } + + return Info; + } + else + { + throw new InvalidSystemResourceException($"Font \"{Name}.ttf\" not found. Please provide it in \"{FontsPath}\"."); + } + } + + FontData = new Dictionary() + { + { SharedFontType.JapanUsEurope, CreateFont("FontStandard") }, + { SharedFontType.SimplifiedChinese, CreateFont("FontChineseSimplified") }, + { SharedFontType.SimplifiedChineseEx, CreateFont("FontExtendedChineseSimplified") }, + { SharedFontType.TraditionalChinese, CreateFont("FontChineseTraditional") }, + { SharedFontType.Korean, CreateFont("FontKorean") }, + { SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") } + }; + + if (FontOffset > Horizon.FontSize) + { + throw new InvalidSystemResourceException( + $"The sum of all fonts size exceed the shared memory size. " + + $"Please make sure that the fonts don't exceed {Horizon.FontSize} bytes in total. " + + $"(actual size: {FontOffset} bytes)."); + } + } + } + + private void WriteMagicAndSize(long Position, int Size) + { + const int DecMagic = 0x18029a7f; + const int Key = 0x49621806; + + int EncryptedSize = EndianSwap.Swap32(Size ^ Key); + + Memory.WriteInt32(Position + 0, DecMagic); + Memory.WriteInt32(Position + 4, EncryptedSize); + } + + public int GetFontSize(SharedFontType FontType) + { + EnsureInitialized(); + + return FontData[FontType].Size; + } + + public int GetSharedMemoryAddressOffset(SharedFontType FontType) + { + EnsureInitialized(); + + return FontData[FontType].Offset + 8; + } + } +} diff --git a/Ryujinx.HLE/Font/SharedFontType.cs b/Ryujinx.HLE/OsHle/Font/SharedFontType.cs similarity index 72% rename from Ryujinx.HLE/Font/SharedFontType.cs rename to Ryujinx.HLE/OsHle/Font/SharedFontType.cs index ca8a42b0c..80f42a7da 100644 --- a/Ryujinx.HLE/Font/SharedFontType.cs +++ b/Ryujinx.HLE/OsHle/Font/SharedFontType.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.HLE.Font +namespace Ryujinx.HLE.OsHle.Font { public enum SharedFontType { @@ -7,6 +7,7 @@ namespace Ryujinx.HLE.Font SimplifiedChineseEx = 2, TraditionalChinese = 3, Korean = 4, - NintendoEx = 5 + NintendoEx = 5, + Count } } \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs b/Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs new file mode 100644 index 000000000..946c51e1d --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + enum AddressSpaceType + { + Addr32Bits = 0, + Addr36Bits = 1, + Addr36BitsNoMap = 2, + Addr39Bits = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs b/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs deleted file mode 100644 index cd3d82237..000000000 --- a/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs +++ /dev/null @@ -1,44 +0,0 @@ -using ChocolArm64.Memory; -using System; -using System.Collections.Generic; - -namespace Ryujinx.HLE.OsHle.Handles -{ - class HSharedMem - { - private List<(AMemory, long, long)> Positions; - - public EventHandler MemoryMapped; - public EventHandler MemoryUnmapped; - - public HSharedMem() - { - Positions = new List<(AMemory, long, long)>(); - } - - public void AddVirtualPosition(AMemory Memory, long Position, long Size) - { - lock (Positions) - { - Positions.Add((Memory, Position, Size)); - - MemoryMapped?.Invoke(this, EventArgs.Empty); - } - } - - public void RemoveVirtualPosition(AMemory Memory, long Position, long Size) - { - lock (Positions) - { - Positions.Remove((Memory, Position, Size)); - - MemoryUnmapped?.Invoke(this, EventArgs.Empty); - } - } - - public (AMemory, long, long)[] GetVirtualPositions() - { - return Positions.ToArray(); - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs b/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs deleted file mode 100644 index 2969a2bae..000000000 --- a/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs +++ /dev/null @@ -1,21 +0,0 @@ -using ChocolArm64.Memory; - -namespace Ryujinx.HLE.OsHle.Handles -{ - class HTransferMem - { - public AMemory Memory { get; private set; } - public AMemoryPerm Perm { get; private set; } - - public long Position { get; private set; } - public long Size { get; private set; } - - public HTransferMem(AMemory Memory, AMemoryPerm Perm, long Position, long Size) - { - this.Memory = Memory; - this.Perm = Perm; - this.Position = Position; - this.Size = Size; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs b/Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs new file mode 100644 index 000000000..e33b6cb9b --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs @@ -0,0 +1,43 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + class KMemoryBlock + { + public long BasePosition { get; set; } + public long PagesCount { get; set; } + + public MemoryState State { get; set; } + public MemoryPermission Permission { get; set; } + public MemoryAttribute Attribute { get; set; } + + public int IpcRefCount { get; set; } + public int DeviceRefCount { get; set; } + + public KMemoryBlock( + long BasePosition, + long PagesCount, + MemoryState State, + MemoryPermission Permission, + MemoryAttribute Attribute) + { + this.BasePosition = BasePosition; + this.PagesCount = PagesCount; + this.State = State; + this.Attribute = Attribute; + this.Permission = Permission; + } + + public KMemoryInfo GetInfo() + { + long Size = PagesCount * KMemoryManager.PageSize; + + return new KMemoryInfo( + BasePosition, + Size, + State, + Permission, + Attribute, + IpcRefCount, + DeviceRefCount); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs b/Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs new file mode 100644 index 000000000..3f79f000d --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs @@ -0,0 +1,33 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + class KMemoryInfo + { + public long Position { get; private set; } + public long Size { get; private set; } + + public MemoryState State { get; private set; } + public MemoryPermission Permission { get; private set; } + public MemoryAttribute Attribute { get; private set; } + + public int IpcRefCount { get; private set; } + public int DeviceRefCount { get; private set; } + + public KMemoryInfo( + long Position, + long Size, + MemoryState State, + MemoryPermission Permission, + MemoryAttribute Attribute, + int IpcRefCount, + int DeviceRefCount) + { + this.Position = Position; + this.Size = Size; + this.State = State; + this.Attribute = Attribute; + this.Permission = Permission; + this.IpcRefCount = IpcRefCount; + this.DeviceRefCount = DeviceRefCount; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs b/Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs new file mode 100644 index 000000000..76c5a53de --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs @@ -0,0 +1,1082 @@ +using ChocolArm64.Memory; +using Ryujinx.HLE.Memory; +using Ryujinx.HLE.OsHle.Kernel; +using System; +using System.Collections.Generic; + +using static Ryujinx.HLE.OsHle.ErrorCode; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class KMemoryManager + { + public const int PageSize = 0x1000; + + private LinkedList Blocks; + + private AMemory CpuMemory; + + private ArenaAllocator Allocator; + + public long AddrSpaceStart { get; private set; } + public long AddrSpaceEnd { get; private set; } + + public long CodeRegionStart { get; private set; } + public long CodeRegionEnd { get; private set; } + + public long MapRegionStart { get; private set; } + public long MapRegionEnd { get; private set; } + + public long HeapRegionStart { get; private set; } + public long HeapRegionEnd { get; private set; } + + public long NewMapRegionStart { get; private set; } + public long NewMapRegionEnd { get; private set; } + + public long TlsIoRegionStart { get; private set; } + public long TlsIoRegionEnd { get; private set; } + + public long PersonalMmHeapUsage { get; private set; } + + private long CurrentHeapAddr; + + public KMemoryManager(Process Process) + { + CpuMemory = Process.Memory; + Allocator = Process.Ns.Memory.Allocator; + + long CodeRegionSize; + long MapRegionSize; + long HeapRegionSize; + long NewMapRegionSize; + long TlsIoRegionSize; + int AddrSpaceWidth; + + AddressSpaceType AddrType = AddressSpaceType.Addr39Bits; + + if (Process.MetaData != null) + { + AddrType = (AddressSpaceType)Process.MetaData.AddressSpaceWidth; + } + + switch (AddrType) + { + case AddressSpaceType.Addr32Bits: + CodeRegionStart = 0x200000; + CodeRegionSize = 0x3fe00000; + MapRegionSize = 0x40000000; + HeapRegionSize = 0x40000000; + NewMapRegionSize = 0; + TlsIoRegionSize = 0; + AddrSpaceWidth = 32; + break; + + case AddressSpaceType.Addr36Bits: + CodeRegionStart = 0x8000000; + CodeRegionSize = 0x78000000; + MapRegionSize = 0x180000000; + HeapRegionSize = 0x180000000; + NewMapRegionSize = 0; + TlsIoRegionSize = 0; + AddrSpaceWidth = 36; + break; + + case AddressSpaceType.Addr36BitsNoMap: + CodeRegionStart = 0x200000; + CodeRegionSize = 0x3fe00000; + MapRegionSize = 0; + HeapRegionSize = 0x80000000; + NewMapRegionSize = 0; + TlsIoRegionSize = 0; + AddrSpaceWidth = 36; + break; + + case AddressSpaceType.Addr39Bits: + CodeRegionStart = 0; + CodeRegionSize = 0x80000000; + MapRegionSize = 0x1000000000; + HeapRegionSize = 0x180000000; + NewMapRegionSize = 0x80000000; + TlsIoRegionSize = 0x1000000000; + AddrSpaceWidth = 39; + break; + + default: throw new InvalidOperationException(); + } + + AddrSpaceStart = 0; + AddrSpaceEnd = 1L << AddrSpaceWidth; + + CodeRegionEnd = CodeRegionStart + CodeRegionSize; + MapRegionStart = CodeRegionEnd; + MapRegionEnd = CodeRegionEnd + MapRegionSize; + HeapRegionStart = MapRegionEnd; + HeapRegionEnd = MapRegionEnd + HeapRegionSize; + NewMapRegionStart = HeapRegionEnd; + NewMapRegionEnd = HeapRegionEnd + NewMapRegionSize; + TlsIoRegionStart = NewMapRegionEnd; + TlsIoRegionEnd = NewMapRegionEnd + TlsIoRegionSize; + + CurrentHeapAddr = HeapRegionStart; + + if (NewMapRegionSize == 0) + { + NewMapRegionStart = AddrSpaceStart; + NewMapRegionEnd = AddrSpaceEnd; + } + + Blocks = new LinkedList(); + + long AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize; + + InsertBlock(AddrSpaceStart, AddrSpacePagesCount, MemoryState.Unmapped); + } + + public void HleMapProcessCode(long Position, long Size) + { + long PagesCount = Size / PageSize; + + if (!Allocator.TryAllocate(Size, out long PA)) + { + throw new InvalidOperationException(); + } + + lock (Blocks) + { + InsertBlock(Position, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute); + + CpuMemory.Map(Position, PA, Size); + } + } + + public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission) + { + long PagesCount = Size / PageSize; + + if (!Allocator.TryAllocate(Size, out long PA)) + { + throw new InvalidOperationException(); + } + + lock (Blocks) + { + InsertBlock(Position, PagesCount, State, Permission); + + CpuMemory.Map(Position, PA, Size); + } + } + + public long HleMapTlsPage() + { + bool HasTlsIoRegion = TlsIoRegionStart != TlsIoRegionEnd; + + long Position = HasTlsIoRegion ? TlsIoRegionStart : CodeRegionStart; + + lock (Blocks) + { + while (Position < (HasTlsIoRegion ? TlsIoRegionEnd : CodeRegionEnd)) + { + if (FindBlock(Position).State == MemoryState.Unmapped) + { + InsertBlock(Position, 1, MemoryState.ThreadLocal, MemoryPermission.ReadAndWrite); + + if (!Allocator.TryAllocate(PageSize, out long PA)) + { + throw new InvalidOperationException(); + } + + CpuMemory.Map(Position, PA, PageSize); + + return Position; + } + + Position += PageSize; + } + + throw new InvalidOperationException(); + } + } + + public long TrySetHeapSize(long Size, out long Position) + { + Position = 0; + + if ((ulong)Size > (ulong)(HeapRegionEnd - HeapRegionStart)) + { + return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory); + } + + bool Success = false; + + long CurrentHeapSize = GetHeapSize(); + + if ((ulong)CurrentHeapSize <= (ulong)Size) + { + //Expand. + long DiffSize = Size - CurrentHeapSize; + + lock (Blocks) + { + if (Success = IsUnmapped(CurrentHeapAddr, DiffSize)) + { + if (!Allocator.TryAllocate(DiffSize, out long PA)) + { + return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory); + } + + long PagesCount = DiffSize / PageSize; + + InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite); + + CpuMemory.Map(CurrentHeapAddr, PA, DiffSize); + } + } + } + else + { + //Shrink. + long FreeAddr = HeapRegionStart + Size; + long DiffSize = CurrentHeapSize - Size; + + lock (Blocks) + { + Success = CheckRange( + FreeAddr, + DiffSize, + MemoryState.Mask, + MemoryState.Heap, + MemoryPermission.Mask, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (Success) + { + long PagesCount = DiffSize / PageSize; + + InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped); + + CpuMemory.Unmap(FreeAddr, DiffSize); + + FreePages(FreeAddr, PagesCount); + } + } + } + + CurrentHeapAddr = HeapRegionStart + Size; + + if (Success) + { + Position = HeapRegionStart; + + return 0; + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long GetHeapSize() + { + return CurrentHeapAddr - HeapRegionStart; + } + + public long SetMemoryAttribute( + long Position, + long Size, + MemoryAttribute AttributeMask, + MemoryAttribute AttributeValue) + { + lock (Blocks) + { + if (CheckRange( + Position, + Size, + MemoryState.AttributeChangeAllowed, + MemoryState.AttributeChangeAllowed, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.BorrowedAndIpcMapped, + MemoryAttribute.None, + MemoryAttribute.DeviceMappedAndUncached, + out MemoryState State, + out MemoryPermission Permission, + out MemoryAttribute Attribute)) + { + long PagesCount = Size / PageSize; + + Attribute &= ~AttributeMask; + Attribute |= AttributeMask & AttributeValue; + + InsertBlock(Position, PagesCount, State, Permission, Attribute); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public KMemoryInfo QueryMemory(long Position) + { + if ((ulong)Position >= (ulong)AddrSpaceStart && + (ulong)Position < (ulong)AddrSpaceEnd) + { + lock (Blocks) + { + return FindBlock(Position).GetInfo(); + } + } + else + { + return new KMemoryInfo( + AddrSpaceEnd, + -AddrSpaceEnd, + MemoryState.Reserved, + MemoryPermission.None, + MemoryAttribute.None, + 0, + 0); + } + } + + public long Map(long Src, long Dst, long Size) + { + bool Success; + + lock (Blocks) + { + Success = CheckRange( + Src, + Size, + MemoryState.MapAllowed, + MemoryState.MapAllowed, + MemoryPermission.Mask, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState SrcState, + out _, + out _); + + Success &= IsUnmapped(Dst, Size); + + if (Success) + { + long PagesCount = Size / PageSize; + + InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed); + + InsertBlock(Dst, PagesCount, MemoryState.MappedMemory, MemoryPermission.ReadAndWrite); + + long PA = CpuMemory.GetPhysicalAddress(Src); + + CpuMemory.Map(Dst, PA, Size); + } + } + + return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long Unmap(long Src, long Dst, long Size) + { + bool Success; + + lock (Blocks) + { + Success = CheckRange( + Src, + Size, + MemoryState.MapAllowed, + MemoryState.MapAllowed, + MemoryPermission.Mask, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.Borrowed, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState SrcState, + out _, + out _); + + Success &= CheckRange( + Dst, + Size, + MemoryState.Mask, + MemoryState.MappedMemory, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (Success) + { + long PagesCount = Size / PageSize; + + InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite); + + InsertBlock(Dst, PagesCount, MemoryState.Unmapped); + + CpuMemory.Unmap(Dst, Size); + } + } + + return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + 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); + + CpuMemory.Map(Position, SharedMemory.PA, SharedMemory.Size); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + 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; + + InsertBlock(Position, PagesCount, MemoryState.Unmapped); + + CpuMemory.Unmap(Position, Size); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long ReserveTransferMemory(long Position, long Size, MemoryPermission Permission) + { + lock (Blocks) + { + if (CheckRange( + Position, + Size, + MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, + MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, + MemoryPermission.Mask, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState State, + out _, + out MemoryAttribute Attribute)) + { + long PagesCount = Size / PageSize; + + Attribute |= MemoryAttribute.Borrowed; + + InsertBlock(Position, PagesCount, State, Permission, Attribute); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long ResetTransferMemory(long Position, long Size) + { + lock (Blocks) + { + if (CheckRange( + Position, + Size, + MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, + MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.Borrowed, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState State, + out _, + out _)) + { + long PagesCount = Size / PageSize; + + InsertBlock(Position, PagesCount, State, MemoryPermission.ReadAndWrite); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long SetProcessMemoryPermission(long Position, long Size, MemoryPermission Permission) + { + lock (Blocks) + { + if (CheckRange( + Position, + Size, + MemoryState.ProcessPermissionChangeAllowed, + MemoryState.ProcessPermissionChangeAllowed, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState State, + out _, + out _)) + { + if (State == MemoryState.CodeStatic) + { + State = MemoryState.CodeMutable; + } + else if (State == MemoryState.ModCodeStatic) + { + State = MemoryState.ModCodeMutable; + } + else + { + throw new InvalidOperationException(); + } + + long PagesCount = Size / PageSize; + + InsertBlock(Position, PagesCount, State, Permission); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long MapPhysicalMemory(long Position, long Size) + { + long End = Position + Size; + + lock (Blocks) + { + long MappedSize = 0; + + KMemoryInfo Info; + + LinkedListNode BaseNode = FindBlockNode(Position); + + LinkedListNode Node = BaseNode; + + do + { + Info = Node.Value.GetInfo(); + + if (Info.State != MemoryState.Unmapped) + { + MappedSize += GetSizeInRange(Info, Position, End); + } + + Node = Node.Next; + } + while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null); + + if (MappedSize == Size) + { + return 0; + } + + long RemainingSize = Size - MappedSize; + + if (!Allocator.TryAllocate(RemainingSize, out long PA)) + { + return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory); + } + + Node = BaseNode; + + do + { + Info = Node.Value.GetInfo(); + + if (Info.State == MemoryState.Unmapped) + { + long CurrSize = GetSizeInRange(Info, Position, End); + + CpuMemory.Map(Info.Position, PA, CurrSize); + + PA += CurrSize; + } + + Node = Node.Next; + } + while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null); + + PersonalMmHeapUsage += RemainingSize; + + long PagesCount = Size / PageSize; + + InsertBlock( + Position, + PagesCount, + MemoryState.Unmapped, + MemoryPermission.None, + MemoryAttribute.None, + MemoryState.Heap, + MemoryPermission.ReadAndWrite, + MemoryAttribute.None); + } + + return 0; + } + + public long UnmapPhysicalMemory(long Position, long Size) + { + long End = Position + Size; + + lock (Blocks) + { + long HeapMappedSize = 0; + + long CurrPosition = Position; + + KMemoryInfo Info; + + LinkedListNode Node = FindBlockNode(CurrPosition); + + do + { + Info = Node.Value.GetInfo(); + + if (Info.State == MemoryState.Heap) + { + if (Info.Attribute != MemoryAttribute.None) + { + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + HeapMappedSize += GetSizeInRange(Info, Position, End); + } + else if (Info.State != MemoryState.Unmapped) + { + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + Node = Node.Next; + } + while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null); + + if (HeapMappedSize == 0) + { + return 0; + } + + PersonalMmHeapUsage -= HeapMappedSize; + + long PagesCount = Size / PageSize; + + InsertBlock(Position, PagesCount, MemoryState.Unmapped); + + CpuMemory.Unmap(Position, Size); + + FreePages(Position, PagesCount); + + return 0; + } + } + + private long GetSizeInRange(KMemoryInfo Info, long Start, long End) + { + long CurrEnd = Info.Size + Info.Position; + long CurrSize = Info.Size; + + if ((ulong)Info.Position < (ulong)Start) + { + CurrSize -= Start - Info.Position; + } + + if ((ulong)CurrEnd > (ulong)End) + { + CurrSize -= CurrEnd - End; + } + + return CurrSize; + } + + private void FreePages(long Position, long PagesCount) + { + for (long Page = 0; Page < PagesCount; Page++) + { + long VA = Position + Page * PageSize; + + long PA = CpuMemory.GetPhysicalAddress(VA); + + Allocator.Free(PA, PageSize); + } + } + + private bool IsUnmapped(long Position, long Size) + { + return CheckRange( + Position, + Size, + MemoryState.Mask, + MemoryState.Unmapped, + MemoryPermission.Mask, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + } + + private bool CheckRange( + long Position, + long Size, + MemoryState StateMask, + MemoryState StateExpected, + MemoryPermission PermissionMask, + MemoryPermission PermissionExpected, + MemoryAttribute AttributeMask, + MemoryAttribute AttributeExpected, + MemoryAttribute AttributeIgnoreMask, + out MemoryState OutState, + out MemoryPermission OutPermission, + out MemoryAttribute OutAttribute) + { + KMemoryInfo BlkInfo = FindBlock(Position).GetInfo(); + + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + if (End <= (ulong)(BlkInfo.Position + BlkInfo.Size)) + { + if ((BlkInfo.Attribute & AttributeMask) == AttributeExpected && + (BlkInfo.State & StateMask) == StateExpected && + (BlkInfo.Permission & PermissionMask) == PermissionExpected) + { + OutState = BlkInfo.State; + OutPermission = BlkInfo.Permission; + OutAttribute = BlkInfo.Attribute & ~AttributeIgnoreMask; + + return true; + } + } + + OutState = MemoryState.Unmapped; + OutPermission = MemoryPermission.None; + OutAttribute = MemoryAttribute.None; + + return false; + } + + private void InsertBlock( + long BasePosition, + long PagesCount, + MemoryState OldState, + MemoryPermission OldPermission, + MemoryAttribute OldAttribute, + MemoryState NewState, + MemoryPermission NewPermission, + MemoryAttribute NewAttribute) + { + //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. + OldAttribute |= MemoryAttribute.IpcAndDeviceMapped; + + ulong Start = (ulong)BasePosition; + ulong End = (ulong)PagesCount * PageSize + Start; + + LinkedListNode Node = Blocks.First; + + while (Node != null) + { + LinkedListNode NewNode = Node; + LinkedListNode NextNode = Node.Next; + + KMemoryBlock CurrBlock = Node.Value; + + ulong CurrStart = (ulong)CurrBlock.BasePosition; + ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart; + + if (Start < CurrEnd && CurrStart < End) + { + MemoryAttribute CurrBlockAttr = CurrBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped; + + if (CurrBlock.State != OldState || + CurrBlock.Permission != OldPermission || + CurrBlockAttr != OldAttribute) + { + Node = NextNode; + + continue; + } + + if (CurrStart >= Start && CurrEnd <= End) + { + CurrBlock.State = NewState; + CurrBlock.Permission = NewPermission; + CurrBlock.Attribute &= ~MemoryAttribute.IpcAndDeviceMapped; + CurrBlock.Attribute |= NewAttribute; + } + else if (CurrStart >= Start) + { + CurrBlock.BasePosition = (long)End; + + CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize); + + long NewPagesCount = (long)((End - CurrStart) / PageSize); + + NewNode = Blocks.AddBefore(Node, new KMemoryBlock( + (long)CurrStart, + NewPagesCount, + NewState, + NewPermission, + NewAttribute)); + } + else if (CurrEnd <= End) + { + CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + + long NewPagesCount = (long)((CurrEnd - Start) / PageSize); + + NewNode = Blocks.AddAfter(Node, new KMemoryBlock( + BasePosition, + NewPagesCount, + NewState, + NewPermission, + NewAttribute)); + } + else + { + CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + + long NextPagesCount = (long)((CurrEnd - End) / PageSize); + + NewNode = Blocks.AddAfter(Node, new KMemoryBlock( + BasePosition, + PagesCount, + NewState, + NewPermission, + NewAttribute)); + + Blocks.AddAfter(NewNode, new KMemoryBlock( + (long)End, + NextPagesCount, + CurrBlock.State, + CurrBlock.Permission, + CurrBlock.Attribute)); + + NextNode = null; + } + + MergeEqualStateNeighbours(NewNode); + } + + Node = NextNode; + } + } + + private void InsertBlock( + long BasePosition, + long 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); + + ulong Start = (ulong)BasePosition; + ulong End = (ulong)PagesCount * PageSize + Start; + + LinkedListNode NewNode = null; + + LinkedListNode Node = Blocks.First; + + while (Node != null) + { + KMemoryBlock CurrBlock = Node.Value; + + LinkedListNode NextNode = Node.Next; + + ulong CurrStart = (ulong)CurrBlock.BasePosition; + ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart; + + if (Start < CurrEnd && CurrStart < End) + { + if (Start >= CurrStart && End <= CurrEnd) + { + Block.Attribute |= CurrBlock.Attribute & MemoryAttribute.IpcAndDeviceMapped; + } + + if (Start > CurrStart && End < CurrEnd) + { + CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + + long NextPagesCount = (long)((CurrEnd - End) / PageSize); + + NewNode = Blocks.AddAfter(Node, Block); + + Blocks.AddAfter(NewNode, new KMemoryBlock( + (long)End, + NextPagesCount, + CurrBlock.State, + CurrBlock.Permission, + CurrBlock.Attribute)); + + break; + } + else if (Start <= CurrStart && End < CurrEnd) + { + CurrBlock.BasePosition = (long)End; + + CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize); + + if (NewNode == null) + { + NewNode = Blocks.AddBefore(Node, Block); + } + } + else if (Start > CurrStart && End >= CurrEnd) + { + CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize); + + if (NewNode == null) + { + NewNode = Blocks.AddAfter(Node, Block); + } + } + else + { + if (NewNode == null) + { + NewNode = Blocks.AddBefore(Node, Block); + } + + Blocks.Remove(Node); + } + } + + Node = NextNode; + } + + if (NewNode == null) + { + NewNode = Blocks.AddFirst(Block); + } + + MergeEqualStateNeighbours(NewNode); + } + + private void MergeEqualStateNeighbours(LinkedListNode Node) + { + KMemoryBlock Block = Node.Value; + + ulong Start = (ulong)Block.BasePosition; + ulong End = (ulong)Block.PagesCount * PageSize + Start; + + if (Node.Previous != null) + { + KMemoryBlock Previous = Node.Previous.Value; + + if (BlockStateEquals(Block, Previous)) + { + Blocks.Remove(Node.Previous); + + Block.BasePosition = Previous.BasePosition; + + Start = (ulong)Block.BasePosition; + } + } + + if (Node.Next != null) + { + KMemoryBlock Next = Node.Next.Value; + + if (BlockStateEquals(Block, Next)) + { + Blocks.Remove(Node.Next); + + End = (ulong)(Next.BasePosition + Next.PagesCount * PageSize); + } + } + + Block.PagesCount = (long)((End - Start) / PageSize); + } + + 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; + } + + private KMemoryBlock FindBlock(long Position) + { + return FindBlockNode(Position)?.Value; + } + + private LinkedListNode FindBlockNode(long Position) + { + ulong Addr = (ulong)Position; + + lock (Blocks) + { + LinkedListNode Node = Blocks.First; + + while (Node != null) + { + KMemoryBlock Block = Node.Value; + + ulong Start = (ulong)Block.BasePosition; + ulong End = (ulong)Block.PagesCount * PageSize + Start; + + if (Start <= Addr && End - 1 >= Addr) + { + return Node; + } + + Node = Node.Next; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs b/Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs new file mode 100644 index 000000000..5c4104747 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + class KSharedMemory + { + public long PA { get; private set; } + public long Size { get; private set; } + + public KSharedMemory(long PA, long Size) + { + this.PA = PA; + this.Size = Size; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs b/Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs new file mode 100644 index 000000000..f116f5489 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs @@ -0,0 +1,60 @@ +using System; + +namespace Ryujinx.HLE.OsHle.Handles +{ + class KTlsPageManager + { + private const int TlsEntrySize = 0x200; + + private long PagePosition; + + private int UsedSlots; + + private bool[] Slots; + + public bool IsEmpty => UsedSlots == 0; + public bool IsFull => UsedSlots == Slots.Length; + + public KTlsPageManager(long PagePosition) + { + this.PagePosition = PagePosition; + + Slots = new bool[KMemoryManager.PageSize / TlsEntrySize]; + } + + public bool TryGetFreeTlsAddr(out long Position) + { + Position = PagePosition; + + for (int Index = 0; Index < Slots.Length; Index++) + { + if (!Slots[Index]) + { + Slots[Index] = true; + + UsedSlots++; + + return true; + } + + Position += TlsEntrySize; + } + + Position = 0; + + return false; + } + + public void FreeTlsSlot(int Slot) + { + if ((uint)Slot > Slots.Length) + { + throw new ArgumentOutOfRangeException(nameof(Slot)); + } + + Slots[Slot] = false; + + UsedSlots--; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs b/Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs new file mode 100644 index 000000000..5f9c04094 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.HLE.OsHle.Handles +{ + class KTransferMemory + { + public long Position { get; private set; } + public long Size { get; private set; } + + public KTransferMemory(long Position, long Size) + { + this.Position = Position; + this.Size = Size; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs b/Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs new file mode 100644 index 000000000..e234d7f08 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs @@ -0,0 +1,22 @@ +using System; + +namespace Ryujinx.HLE.OsHle.Handles +{ + [Flags] + enum MemoryAttribute : byte + { + None = 0, + Mask = 0xff, + + Borrowed = 1 << 0, + IpcMapped = 1 << 1, + DeviceMapped = 1 << 2, + Uncached = 1 << 3, + + IpcAndDeviceMapped = IpcMapped | DeviceMapped, + + BorrowedAndIpcMapped = Borrowed | IpcMapped, + + DeviceMappedAndUncached = DeviceMapped | Uncached + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs b/Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs new file mode 100644 index 000000000..416f171e2 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs @@ -0,0 +1,18 @@ +using System; + +namespace Ryujinx.HLE.OsHle.Handles +{ + [Flags] + enum MemoryPermission : byte + { + None = 0, + Mask = 0xff, + + Read = 1 << 0, + Write = 1 << 1, + Execute = 1 << 2, + + ReadAndWrite = Read | Write, + ReadAndExecute = Read | Execute + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Handles/MemoryState.cs b/Ryujinx.HLE/OsHle/Handles/MemoryState.cs new file mode 100644 index 000000000..5437d6a33 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Handles/MemoryState.cs @@ -0,0 +1,49 @@ +using System; + +namespace Ryujinx.HLE.OsHle +{ + [Flags] + enum MemoryState : uint + { + Unmapped = 0x00000000, + Io = 0x00002001, + Normal = 0x00042002, + CodeStatic = 0x00DC7E03, + CodeMutable = 0x03FEBD04, + Heap = 0x037EBD05, + SharedMemory = 0x00402006, + ModCodeStatic = 0x00DD7E08, + ModCodeMutable = 0x03FFBD09, + IpcBuffer0 = 0x005C3C0A, + MappedMemory = 0x005C3C0B, + ThreadLocal = 0x0040200C, + TransferMemoryIsolated = 0x015C3C0D, + TransferMemory = 0x005C380E, + ProcessMemory = 0x0040380F, + Reserved = 0x00000010, + IpcBuffer1 = 0x005C3811, + IpcBuffer3 = 0x004C2812, + KernelStack = 0x00002013, + CodeReadOnly = 0x00402214, + CodeWritable = 0x00402015, + Mask = 0xffffffff, + + PermissionChangeAllowed = 1 << 8, + ForceReadWritableByDebugSyscalls = 1 << 9, + IpcSendAllowedType0 = 1 << 10, + IpcSendAllowedType3 = 1 << 11, + IpcSendAllowedType1 = 1 << 12, + ProcessPermissionChangeAllowed = 1 << 14, + MapAllowed = 1 << 15, + UnmapProcessCodeMemoryAllowed = 1 << 16, + TransferMemoryAllowed = 1 << 17, + QueryPhysicalAddressAllowed = 1 << 18, + MapDeviceAllowed = 1 << 19, + MapDeviceAlignedAllowed = 1 << 20, + IpcBufferAllowed = 1 << 21, + IsPoolAllocated = 1 << 22, + MapProcessAllowed = 1 << 23, + AttributeChangeAllowed = 1 << 24, + CodeMemoryAllowed = 1 << 25 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Homebrew.cs b/Ryujinx.HLE/OsHle/Homebrew.cs index 778e52fe5..90c6c4c64 100644 --- a/Ryujinx.HLE/OsHle/Homebrew.cs +++ b/Ryujinx.HLE/OsHle/Homebrew.cs @@ -10,23 +10,23 @@ namespace Ryujinx.HLE.OsHle //http://switchbrew.org/index.php?title=Homebrew_ABI public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle, string SwitchPath) { - Memory.Manager.Map(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW); - - //MainThreadHandle + //MainThreadHandle. WriteConfigEntry(Memory, ref Position, 1, 0, MainThreadHandle); - //NextLoadPath + //NextLoadPath. WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400); - // Argv + //Argv. long ArgvPosition = Position + 0xC00; - WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition); + Memory.WriteBytes(ArgvPosition, Encoding.ASCII.GetBytes(SwitchPath + "\0")); - //AppletType + WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition); + + //AppletType. WriteConfigEntry(Memory, ref Position, 7); - //EndOfList + //EndOfList. WriteConfigEntry(Memory, ref Position, 0); } diff --git a/Ryujinx.HLE/OsHle/Horizon.cs b/Ryujinx.HLE/OsHle/Horizon.cs index b4c7d36ea..1fd210dd5 100644 --- a/Ryujinx.HLE/OsHle/Horizon.cs +++ b/Ryujinx.HLE/OsHle/Horizon.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Npdm; using Ryujinx.HLE.Logging; +using Ryujinx.HLE.OsHle.Font; using Ryujinx.HLE.OsHle.Handles; using Ryujinx.HLE.OsHle.SystemState; using System; @@ -12,7 +13,7 @@ namespace Ryujinx.HLE.OsHle public class Horizon : IDisposable { internal const int HidSize = 0x40000; - internal const int FontSize = 0x50; + internal const int FontSize = 0x1100000; private Switch Ns; @@ -22,10 +23,10 @@ namespace Ryujinx.HLE.OsHle public SystemStateMgr SystemState { get; private set; } - internal MemoryAllocator Allocator { get; private set; } + internal KSharedMemory HidSharedMem { get; private set; } + internal KSharedMemory FontSharedMem { get; private set; } - internal HSharedMem HidSharedMem { get; private set; } - internal HSharedMem FontSharedMem { get; private set; } + internal SharedFontManager Font { get; private set; } internal KEvent VsyncEvent { get; private set; } @@ -39,10 +40,16 @@ namespace Ryujinx.HLE.OsHle SystemState = new SystemStateMgr(); - Allocator = new MemoryAllocator(); + if (!Ns.Memory.Allocator.TryAllocate(HidSize, out long HidPA) || + !Ns.Memory.Allocator.TryAllocate(FontSize, out long FontPA)) + { + throw new InvalidOperationException(); + } - HidSharedMem = new HSharedMem(); - FontSharedMem = new HSharedMem(); + HidSharedMem = new KSharedMemory(HidPA, HidSize); + FontSharedMem = new KSharedMemory(FontPA, FontSize); + + Font = new SharedFontManager(Ns, FontSharedMem.PA); VsyncEvent = new KEvent(); } @@ -54,7 +61,25 @@ namespace Ryujinx.HLE.OsHle Ns.VFs.LoadRomFs(RomFsFile); } - Process MainProcess = MakeProcess(); + string NpdmFileName = Path.Combine(ExeFsDir, "main.npdm"); + + Npdm MetaData = null; + + if (File.Exists(NpdmFileName)) + { + Ns.Log.PrintInfo(LogClass.Loader, $"Loading main.npdm..."); + + using (FileStream Input = new FileStream(NpdmFileName, FileMode.Open)) + { + MetaData = new Npdm(Input); + } + } + else + { + Ns.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); + } + + Process MainProcess = MakeProcess(MetaData); void LoadNso(string FileName) { @@ -78,21 +103,7 @@ namespace Ryujinx.HLE.OsHle } } - void LoadNpdm(string FileName) - { - string File = Directory.GetFiles(ExeFsDir, FileName)[0]; - - Ns.Log.PrintInfo(LogClass.Loader, "Loading Title Metadata..."); - - using (FileStream Input = new FileStream(File, FileMode.Open)) - { - MainProcess.Metadata = new Npdm(Input); - } - } - - LoadNpdm("*.npdm"); - - if (!MainProcess.Metadata.Is64Bits) + if (!MainProcess.MetaData.Is64Bits) { throw new NotImplementedException("32-bit titles are unsupported!"); } @@ -145,7 +156,7 @@ namespace Ryujinx.HLE.OsHle public void SignalVsync() => VsyncEvent.WaitEvent.Set(); - private Process MakeProcess() + private Process MakeProcess(Npdm MetaData = null) { Process Process; @@ -158,7 +169,7 @@ namespace Ryujinx.HLE.OsHle ProcessId++; } - Process = new Process(Ns, Scheduler, ProcessId); + Process = new Process(Ns, Scheduler, ProcessId, MetaData); Processes.TryAdd(ProcessId, Process); } diff --git a/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs b/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs index bbae53255..a62fc1bfe 100644 --- a/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs +++ b/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs @@ -2,18 +2,21 @@ namespace Ryujinx.HLE.OsHle.Kernel { static class KernelErr { - public const int InvalidAlignment = 102; - public const int InvalidAddress = 106; - public const int InvalidMemRange = 110; - public const int InvalidPriority = 112; - public const int InvalidCoreId = 113; - public const int InvalidHandle = 114; - public const int InvalidCoreMask = 116; - public const int Timeout = 117; - public const int Canceled = 118; - public const int CountOutOfRange = 119; - public const int InvalidEnumValue = 120; - public const int InvalidThread = 122; - public const int InvalidState = 125; + public const int InvalidSize = 101; + public const int InvalidAddress = 102; + public const int OutOfMemory = 104; + public const int NoAccessPerm = 106; + public const int InvalidPermission = 108; + public const int InvalidMemRange = 110; + public const int InvalidPriority = 112; + public const int InvalidCoreId = 113; + public const int InvalidHandle = 114; + public const int InvalidMaskValue = 116; + public const int Timeout = 117; + public const int Canceled = 118; + public const int CountOutOfRange = 119; + public const int InvalidEnumValue = 120; + public const int InvalidThread = 122; + public const int InvalidState = 125; } } \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs index 6f7bc42fd..a33ffe5e7 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs @@ -10,7 +10,7 @@ using System.Threading; namespace Ryujinx.HLE.OsHle.Kernel { - partial class SvcHandler : IDisposable + partial class SvcHandler { private delegate void SvcFunc(AThreadState ThreadState); @@ -22,10 +22,6 @@ namespace Ryujinx.HLE.OsHle.Kernel private ConcurrentDictionary SyncWaits; - private HashSet<(HSharedMem, long, long)> MappedSharedMems; - - private ulong CurrentHeapSize; - private const uint SelfThreadHandle = 0xffff8000; private const uint SelfProcessHandle = 0xffff8001; @@ -82,8 +78,6 @@ namespace Ryujinx.HLE.OsHle.Kernel this.Memory = Process.Memory; SyncWaits = new ConcurrentDictionary(); - - MappedSharedMems = new HashSet<(HSharedMem, long, long)>(); } static SvcHandler() @@ -126,26 +120,5 @@ namespace Ryujinx.HLE.OsHle.Kernel return Process.HandleTable.GetData(Handle); } } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing) - { - lock (MappedSharedMems) - { - foreach ((HSharedMem SharedMem, long Position, long Size) in MappedSharedMems) - { - SharedMem.RemoveVirtualPosition(Memory, Position, Size); - } - - MappedSharedMems.Clear(); - } - } - } } } \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs b/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs index f10cad7a2..68d872930 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs @@ -1,4 +1,3 @@ -using ChocolArm64.Memory; using ChocolArm64.State; using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Handles; @@ -11,43 +10,85 @@ namespace Ryujinx.HLE.OsHle.Kernel { private void SvcSetHeapSize(AThreadState ThreadState) { - uint Size = (uint)ThreadState.X1; + long Size = (long)ThreadState.X1; - long Position = MemoryRegions.HeapRegionAddress; - - if (Size > CurrentHeapSize) + if ((Size & 0x1fffff) != 0 || Size != (uint)Size) { - Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + long Result = Process.MemoryManager.TrySetHeapSize(Size, out long Position); + + ThreadState.X0 = (ulong)Result; + + if (Result == 0) + { + ThreadState.X1 = (ulong)Position; } else { - Memory.Manager.Unmap(Position + Size, (long)CurrentHeapSize - Size); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } - - CurrentHeapSize = Size; - - ThreadState.X0 = 0; - ThreadState.X1 = (ulong)Position; } private void SvcSetMemoryAttribute(AThreadState ThreadState) { long Position = (long)ThreadState.X0; long Size = (long)ThreadState.X1; - int State0 = (int)ThreadState.X2; - int State1 = (int)ThreadState.X3; - if ((State0 == 0 && State1 == 0) || - (State0 == 8 && State1 == 0)) + if (!PageAligned(Position)) { - Memory.Manager.ClearAttrBit(Position, Size, 3); - } - else if (State0 == 8 && State1 == 8) - { - Memory.Manager.SetAttrBit(Position, Size, 3); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; } - ThreadState.X0 = 0; + if (!PageAligned(Size) || Size == 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + MemoryAttribute AttributeMask = (MemoryAttribute)ThreadState.X2; + MemoryAttribute AttributeValue = (MemoryAttribute)ThreadState.X3; + + MemoryAttribute Attributes = AttributeMask | AttributeValue; + + if (Attributes != AttributeMask || + (Attributes | MemoryAttribute.Uncached) != MemoryAttribute.Uncached) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); + + return; + } + + long Result = Process.MemoryManager.SetMemoryAttribute( + Position, + Size, + AttributeMask, + AttributeValue); + + if (Result != 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + else + { + Memory.StopObservingRegion(Position, Size); + } + + ThreadState.X0 = (ulong)Result; } private void SvcMapMemory(AThreadState ThreadState) @@ -56,33 +97,59 @@ namespace Ryujinx.HLE.OsHle.Kernel long Src = (long)ThreadState.X1; long Size = (long)ThreadState.X2; - if (!IsValidPosition(Src)) + if (!PageAligned(Src | Dst)) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid src address {Src:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideAddrSpace(Src, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideNewMapRegion(Dst, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); return; } - if (!IsValidMapPosition(Dst)) + long Result = Process.MemoryManager.Map(Src, Dst, Size); + + if (Result != 0) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); - - return; + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } - AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src); - - Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm); - - Memory.Manager.Reprotect(Src, Size, AMemoryPerm.None); - - Memory.Manager.SetAttrBit(Src, Size, 0); - - ThreadState.X0 = 0; + ThreadState.X0 = (ulong)Result; } private void SvcUnmapMemory(AThreadState ThreadState) @@ -91,33 +158,59 @@ namespace Ryujinx.HLE.OsHle.Kernel long Src = (long)ThreadState.X1; long Size = (long)ThreadState.X2; - if (!IsValidPosition(Src)) + if (!PageAligned(Src | Dst)) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid src address {Src:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideAddrSpace(Src, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideNewMapRegion(Dst, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); return; } - if (!IsValidMapPosition(Dst)) + long Result = Process.MemoryManager.Unmap(Src, Dst, Size); + + if (Result != 0) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); - - return; + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } - AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst); - - Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory); - - Memory.Manager.Reprotect(Src, Size, DstInfo.Perm); - - Memory.Manager.ClearAttrBit(Src, Size, 0); - - ThreadState.X0 = 0; + ThreadState.X0 = (ulong)Result; } private void SvcQueryMemory(AThreadState ThreadState) @@ -125,26 +218,16 @@ namespace Ryujinx.HLE.OsHle.Kernel long InfoPtr = (long)ThreadState.X0; long Position = (long)ThreadState.X2; - AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); + KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position); - if (MapInfo == null) - { - long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; - - long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1; - - MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None); - } - - Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position); - Memory.WriteInt64(InfoPtr + 0x08, MapInfo.Size); - Memory.WriteInt32(InfoPtr + 0x10, MapInfo.Type); - Memory.WriteInt32(InfoPtr + 0x14, MapInfo.Attr); - Memory.WriteInt32(InfoPtr + 0x18, (int)MapInfo.Perm); - Memory.WriteInt32(InfoPtr + 0x1c, 0); - Memory.WriteInt32(InfoPtr + 0x20, 0); + 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); - //TODO: X1. ThreadState.X0 = 0; ThreadState.X1 = 0; @@ -152,134 +235,344 @@ namespace Ryujinx.HLE.OsHle.Kernel private void SvcMapSharedMemory(AThreadState ThreadState) { - int Handle = (int)ThreadState.X0; - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - int Perm = (int)ThreadState.X3; + int Handle = (int)ThreadState.X0; + long Position = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; - if (!IsValidPosition(Src)) + if (!PageAligned(Position)) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } - HSharedMem SharedMem = Process.HandleTable.GetData(Handle); - - if (SharedMem != null) + if (!PageAligned(Size) || Size == 0) { - Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); - AMemoryHelper.FillWithZeros(Memory, Src, (int)Size); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); - SharedMem.AddVirtualPosition(Memory, Src, Size); - - Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); - - lock (MappedSharedMems) - { - MappedSharedMems.Add((SharedMem, Src, Size)); - } - - ThreadState.X0 = 0; + return; } - //TODO: Error codes. + if ((ulong)(Position + Size) <= (ulong)Position) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + MemoryPermission Permission = (MemoryPermission)ThreadState.X3; + + if ((Permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission); + + return; + } + + KSharedMemory SharedMemory = Process.HandleTable.GetData(Handle); + + if (SharedMemory == null) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } + + if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (SharedMemory.Size != Size) + { + Ns.Log.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); + + if (Result != 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } private void SvcUnmapSharedMemory(AThreadState ThreadState) { - int Handle = (int)ThreadState.X0; - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; + int Handle = (int)ThreadState.X0; + long Position = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; - if (!IsValidPosition(Src)) + if (!PageAligned(Position)) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } - HSharedMem SharedMem = Process.HandleTable.GetData(Handle); - - if (SharedMem != null) + if (!PageAligned(Size) || Size == 0) { - Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); - SharedMem.RemoveVirtualPosition(Memory, Src, Size); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); - lock (MappedSharedMems) - { - MappedSharedMems.Remove((SharedMem, Src, Size)); - } - - ThreadState.X0 = 0; + return; } - //TODO: Error codes. + if ((ulong)(Position + Size) <= (ulong)Position) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + KSharedMemory SharedMemory = Process.HandleTable.GetData(Handle); + + if (SharedMemory == null) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } + + if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + long Result = Process.MemoryManager.UnmapSharedMemory(Position, Size); + + if (Result != 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } private void SvcCreateTransferMemory(AThreadState ThreadState) { - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - int Perm = (int)ThreadState.X3; + long Position = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; - if (!IsValidPosition(Src)) + if (!PageAligned(Position)) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } - AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src); + if (!PageAligned(Size) || Size == 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); - Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); - HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size); + return; + } - ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem); + if ((ulong)(Position + Size) <= (ulong)Position) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + MemoryPermission Permission = (MemoryPermission)ThreadState.X3; + + if (Permission > MemoryPermission.ReadAndWrite || Permission == MemoryPermission.Write) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission); + + return; + } + + Process.MemoryManager.ReserveTransferMemory(Position, Size, Permission); + + KTransferMemory TransferMemory = new KTransferMemory(Position, Size); + + int Handle = Process.HandleTable.OpenHandle(TransferMemory); ThreadState.X0 = 0; - ThreadState.X1 = Handle; + ThreadState.X1 = (ulong)Handle; } private void SvcMapPhysicalMemory(AThreadState ThreadState) { long Position = (long)ThreadState.X0; - uint Size = (uint)ThreadState.X1; + long Size = (long)ThreadState.X1; - Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW); + if (!PageAligned(Position)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); - ThreadState.X0 = 0; + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Position + Size) <= (ulong)Position) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideAddrSpace(Position, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + long Result = Process.MemoryManager.MapPhysicalMemory(Position, Size); + + if (Result != 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } private void SvcUnmapPhysicalMemory(AThreadState ThreadState) { long Position = (long)ThreadState.X0; - uint Size = (uint)ThreadState.X1; + long Size = (long)ThreadState.X1; - Memory.Manager.Unmap(Position, Size); + if (!PageAligned(Position)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); - ThreadState.X0 = 0; + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (!PageAligned(Size) || Size == 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); + + return; + } + + if ((ulong)(Position + Size) <= (ulong)Position) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + if (!InsideAddrSpace(Position, Size)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return; + } + + long Result = Process.MemoryManager.UnmapPhysicalMemory(Position, Size); + + if (Result != 0) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } - private static bool IsValidPosition(long Position) + private static bool PageAligned(long Position) { - return Position >= MemoryRegions.AddrSpaceStart && - Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; + return (Position & (KMemoryManager.PageSize - 1)) == 0; } - private static bool IsValidMapPosition(long Position) + private bool InsideAddrSpace(long Position, long Size) { - return Position >= MemoryRegions.MapRegionAddress && - Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize; + 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/OsHle/Kernel/SvcSystem.cs b/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs index 08305522f..f833745b7 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs @@ -18,8 +18,6 @@ namespace Ryujinx.HLE.OsHle.Kernel private const bool EnableProcessDebugging = false; - private const bool IsVirtualMemoryEnabled = true; //This is always true(?) - private void SvcExitProcess(AThreadState ThreadState) { Ns.Os.ExitProcess(ThreadState.ProcessId); @@ -53,12 +51,11 @@ namespace Ryujinx.HLE.OsHle.Kernel { Session.Dispose(); } - else if (Obj is HTransferMem TMem) + else if (Obj is KTransferMemory TransferMemory) { - TMem.Memory.Manager.Reprotect( - TMem.Position, - TMem.Size, - TMem.Perm); + Process.MemoryManager.ResetTransferMemory( + TransferMemory.Position, + TransferMemory.Size); } ThreadState.X0 = 0; @@ -306,27 +303,29 @@ namespace Ryujinx.HLE.OsHle.Kernel break; case 2: - ThreadState.X1 = MemoryRegions.MapRegionAddress; + ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionStart; break; case 3: - ThreadState.X1 = MemoryRegions.MapRegionSize; + ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionEnd - + (ulong)Process.MemoryManager.MapRegionStart; break; case 4: - ThreadState.X1 = MemoryRegions.HeapRegionAddress; + ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionStart; break; case 5: - ThreadState.X1 = MemoryRegions.HeapRegionSize; + ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionEnd - + (ulong)Process.MemoryManager.HeapRegionStart; break; case 6: - ThreadState.X1 = MemoryRegions.TotalMemoryAvailable; + ThreadState.X1 = (ulong)Process.Ns.Memory.Allocator.TotalAvailableSize; break; case 7: - ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize; + ThreadState.X1 = (ulong)Process.Ns.Memory.Allocator.TotalUsedSize; break; case 8: @@ -338,23 +337,29 @@ namespace Ryujinx.HLE.OsHle.Kernel break; case 12: - ThreadState.X1 = MemoryRegions.AddrSpaceStart; + ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceStart; break; case 13: - ThreadState.X1 = MemoryRegions.AddrSpaceSize; + ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceEnd - + (ulong)Process.MemoryManager.AddrSpaceStart; break; case 14: - ThreadState.X1 = MemoryRegions.MapRegionAddress; + ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionStart; break; case 15: - ThreadState.X1 = MemoryRegions.MapRegionSize; + ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionEnd - + (ulong)Process.MemoryManager.NewMapRegionStart; break; case 16: - ThreadState.X1 = IsVirtualMemoryEnabled ? 1 : 0; + ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0); + break; + + case 17: + ThreadState.X1 = (ulong)Process.MemoryManager.PersonalMmHeapUsage; break; default: diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs b/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs index 8702203e4..045248504 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs @@ -204,7 +204,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreMask); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); return; } @@ -226,7 +226,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreMask); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); return; } diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs index 9fc426176..164a85cac 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs @@ -26,7 +26,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } @@ -79,7 +79,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } @@ -115,7 +115,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } @@ -124,7 +124,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } @@ -214,7 +214,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } @@ -223,7 +223,7 @@ namespace Ryujinx.HLE.OsHle.Kernel { Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!"); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } diff --git a/Ryujinx.HLE/OsHle/MemoryAllocator.cs b/Ryujinx.HLE/OsHle/MemoryAllocator.cs deleted file mode 100644 index 8696aa4d1..000000000 --- a/Ryujinx.HLE/OsHle/MemoryAllocator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Ryujinx.HLE.OsHle -{ - class MemoryAllocator - { - public bool TryAllocate(long Size, out long Address) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/MemoryRegions.cs b/Ryujinx.HLE/OsHle/MemoryRegions.cs deleted file mode 100644 index 198c621cc..000000000 --- a/Ryujinx.HLE/OsHle/MemoryRegions.cs +++ /dev/null @@ -1,29 +0,0 @@ -using ChocolArm64.Memory; - -namespace Ryujinx.HLE.OsHle -{ - static class MemoryRegions - { - public const long AddrSpaceStart = 0x08000000; - - public const long MapRegionAddress = 0x10000000; - public const long MapRegionSize = 0x20000000; - - public const long HeapRegionAddress = MapRegionAddress + MapRegionSize; - public const long HeapRegionSize = TlsPagesAddress - HeapRegionAddress; - - public const long MainStackSize = 0x100000; - - public const long MainStackAddress = AMemoryMgr.AddrSize - MainStackSize; - - public const long TlsPagesSize = 0x20000; - - public const long TlsPagesAddress = MainStackAddress - TlsPagesSize; - - public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize; - - public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart; - - public const long AddrSpaceSize = AMemoryMgr.AddrSize - AddrSpaceStart; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Process.cs b/Ryujinx.HLE/OsHle/Process.cs index 6cbc08604..96fe3610d 100644 --- a/Ryujinx.HLE/OsHle/Process.cs +++ b/Ryujinx.HLE/OsHle/Process.cs @@ -12,6 +12,7 @@ using Ryujinx.HLE.OsHle.Handles; using Ryujinx.HLE.OsHle.Kernel; using Ryujinx.HLE.OsHle.Services.Nv; using Ryujinx.HLE.OsHle.SystemState; +using Ryujinx.HLE.OsHle.Utilities; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -22,13 +23,9 @@ namespace Ryujinx.HLE.OsHle { class Process : IDisposable { - private const int TlsSize = 0x200; - - private const int TotalTlsSlots = (int)MemoryRegions.TlsPagesSize / TlsSize; - private const int TickFreq = 19_200_000; - private Switch Ns; + public Switch Ns { get; private set; } public bool NeedsHbAbi { get; private set; } @@ -40,22 +37,24 @@ namespace Ryujinx.HLE.OsHle public AMemory Memory { get; private set; } + public KMemoryManager MemoryManager { get; private set; } + + private List TlsPages; + public KProcessScheduler Scheduler { get; private set; } public List ThreadArbiterList { get; private set; } public object ThreadSyncLock { get; private set; } + public Npdm MetaData { get; private set; } + public KProcessHandleTable HandleTable { get; private set; } public AppletStateMgr AppletState { get; private set; } - public Npdm Metadata { get; set; } - private SvcHandler SvcHandler; - private ConcurrentDictionary TlsSlots; - private ConcurrentDictionary Threads; private KThread MainThread; @@ -70,13 +69,18 @@ namespace Ryujinx.HLE.OsHle private bool Disposed; - public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId) + public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId, Npdm MetaData) { this.Ns = Ns; this.Scheduler = Scheduler; + this.MetaData = MetaData; this.ProcessId = ProcessId; - Memory = new AMemory(); + Memory = new AMemory(Ns.Memory.RamPointer); + + MemoryManager = new KMemoryManager(this); + + TlsPages = new List(); ThreadArbiterList = new List(); @@ -88,18 +92,11 @@ namespace Ryujinx.HLE.OsHle SvcHandler = new SvcHandler(Ns, this); - TlsSlots = new ConcurrentDictionary(); - Threads = new ConcurrentDictionary(); Executables = new List(); - ImageBase = MemoryRegions.AddrSpaceStart; - - MapRWMemRegion( - MemoryRegions.TlsPagesAddress, - MemoryRegions.TlsPagesSize, - MemoryType.ThreadLocal); + ImageBase = MemoryManager.CodeRegionStart; } public void LoadProgram(IExecutable Program) @@ -111,17 +108,17 @@ namespace Ryujinx.HLE.OsHle Ns.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ImageBase:x16}."); - Executable Executable = new Executable(Program, Memory, ImageBase); + Executable Executable = new Executable(Program, MemoryManager, Memory, ImageBase); Executables.Add(Executable); - ImageBase = AMemoryHelper.PageRoundUp(Executable.ImageEnd); + ImageBase = IntUtils.AlignUp(Executable.ImageEnd, KMemoryManager.PageSize); } public void SetEmptyArgs() { //TODO: This should be part of Run. - ImageBase += AMemoryMgr.PageSize; + ImageBase += KMemoryManager.PageSize; } public bool Run(bool NeedsHbAbi = false) @@ -140,14 +137,19 @@ namespace Ryujinx.HLE.OsHle MakeSymbolTable(); - MapRWMemRegion( - MemoryRegions.MainStackAddress, - MemoryRegions.MainStackSize, - MemoryType.Normal); + long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize; - long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize; + long MainStackSize = 1 * 1024 * 1024; - int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 44, 0); + long MainStackBottom = MainStackTop - MainStackSize; + + MemoryManager.HleMapCustom( + MainStackBottom, + MainStackSize, + MemoryState.MappedMemory, + MemoryPermission.ReadAndWrite); + + int Handle = MakeThread(Executables[0].ImageBase, MainStackTop, 0, 44, 0); if (Handle == -1) { @@ -158,7 +160,15 @@ namespace Ryujinx.HLE.OsHle if (NeedsHbAbi) { - HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); + HbAbiDataPosition = IntUtils.AlignUp(Executables[0].ImageEnd, KMemoryManager.PageSize); + + const long HbAbiDataSize = KMemoryManager.PageSize; + + MemoryManager.HleMapCustom( + HbAbiDataPosition, + HbAbiDataSize, + MemoryState.MappedMemory, + MemoryPermission.ReadAndWrite); string SwitchPath = Ns.VFs.SystemPathToSwitchPath(Executables[0].FilePath); @@ -173,11 +183,6 @@ namespace Ryujinx.HLE.OsHle return true; } - private void MapRWMemRegion(long Position, long Size, MemoryType Type) - { - Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW); - } - public void StopAllThreadsAsync() { if (Disposed) @@ -190,9 +195,9 @@ namespace Ryujinx.HLE.OsHle MainThread.Thread.StopExecution(); } - foreach (AThread Thread in TlsSlots.Values) + foreach (KThread Thread in Threads.Values) { - Thread.StopExecution(); + Thread.Thread.StopExecution(); } } @@ -216,9 +221,9 @@ namespace Ryujinx.HLE.OsHle int Handle = HandleTable.OpenHandle(Thread); - int ThreadId = GetFreeTlsSlot(CpuThread); + long Tpidr = GetFreeTls(); - long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize; + int ThreadId = (int)((Tpidr - MemoryManager.TlsIoRegionStart) / 0x200) + 1; CpuThread.ThreadState.ProcessId = ProcessId; CpuThread.ThreadState.ThreadId = ThreadId; @@ -240,6 +245,32 @@ namespace Ryujinx.HLE.OsHle return Handle; } + private long GetFreeTls() + { + long Position; + + lock (TlsPages) + { + for (int Index = 0; Index < TlsPages.Count; Index++) + { + if (TlsPages[Index].TryGetFreeTlsAddr(out Position)) + { + return Position; + } + } + + long PagePosition = MemoryManager.HleMapTlsPage(); + + KTlsPageManager TlsPage = new KTlsPageManager(PagePosition); + + TlsPages.Add(TlsPage); + + TlsPage.TryGetFreeTlsAddr(out Position); + } + + return Position; + } + private void BreakHandler(object sender, AInstExceptionEventArgs e) { throw new GuestBrokeExecutionException(); @@ -346,25 +377,10 @@ namespace Ryujinx.HLE.OsHle return Name; } - private int GetFreeTlsSlot(AThread Thread) - { - for (int Index = 1; Index < TotalTlsSlots; Index++) - { - if (TlsSlots.TryAdd(Index, Thread)) - { - return Index; - } - } - - throw new InvalidOperationException(); - } - private void ThreadFinished(object sender, EventArgs e) { if (sender is AThread Thread) { - TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); - Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread); Scheduler.RemoveThread(KernelThread); @@ -372,7 +388,7 @@ namespace Ryujinx.HLE.OsHle KernelThread.WaitEvent.Set(); } - if (TlsSlots.Count == 0) + if (Threads.Count == 0) { if (ShouldDispose) { @@ -383,11 +399,6 @@ namespace Ryujinx.HLE.OsHle } } - private int GetTlsSlot(long Position) - { - return (int)((Position - MemoryRegions.TlsPagesAddress) / TlsSize); - } - public KThread GetThread(long Tpidr) { if (!Threads.TryGetValue(Tpidr, out KThread Thread)) @@ -411,7 +422,7 @@ namespace Ryujinx.HLE.OsHle //safe as the thread may try to access those resources. Instead, we set //the flag to have the Process disposed when all threads finishes. //Note: This may not happen if the guest code gets stuck on a infinite loop. - if (TlsSlots.Count > 0) + if (Threads.Count > 0) { ShouldDispose = true; @@ -439,10 +450,6 @@ namespace Ryujinx.HLE.OsHle AppletState.Dispose(); - SvcHandler.Dispose(); - - Memory.Dispose(); - Ns.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting..."); } } diff --git a/Ryujinx.HLE/OsHle/Services/Hid/IAppletResource.cs b/Ryujinx.HLE/OsHle/Services/Hid/IAppletResource.cs index 2ef67cc3a..5c32ca837 100644 --- a/Ryujinx.HLE/OsHle/Services/Hid/IAppletResource.cs +++ b/Ryujinx.HLE/OsHle/Services/Hid/IAppletResource.cs @@ -10,9 +10,9 @@ namespace Ryujinx.HLE.OsHle.Services.Hid public override IReadOnlyDictionary Commands => m_Commands; - private HSharedMem HidSharedMem; + private KSharedMemory HidSharedMem; - public IAppletResource(HSharedMem HidSharedMem) + public IAppletResource(KSharedMemory HidSharedMem) { m_Commands = new Dictionary() { diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASCtx.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASCtx.cs new file mode 100644 index 000000000..e718182a3 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASCtx.cs @@ -0,0 +1,198 @@ +using ChocolArm64.Memory; +using Ryujinx.HLE.Gpu.Memory; +using System.Collections.Generic; + +namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS +{ + class NvGpuASCtx + { + public NvGpuVmm Vmm { get; private set; } + + private class Range + { + public ulong Start { get; private set; } + public ulong End { get; private set; } + + public Range(long Position, long Size) + { + Start = (ulong)Position; + End = (ulong)Size + Start; + } + } + + private class MappedMemory : Range + { + public long PhysicalAddress { get; private set; } + public bool VaAllocated { get; private set; } + + public MappedMemory( + long Position, + long Size, + long PhysicalAddress, + bool VaAllocated) : base(Position, Size) + { + this.PhysicalAddress = PhysicalAddress; + this.VaAllocated = VaAllocated; + } + } + + private SortedList Maps; + private SortedList Reservations; + + public NvGpuASCtx(ServiceCtx Context) + { + Vmm = new NvGpuVmm(Context.Memory); + + Maps = new SortedList(); + Reservations = new SortedList(); + } + + public bool ValidateFixedBuffer(long Position, long Size) + { + long MapEnd = Position + Size; + + //Check if size is valid (0 is also not allowed). + if ((ulong)MapEnd <= (ulong)Position) + { + return false; + } + + //Check if address is page aligned. + if ((Position & NvGpuVmm.PageMask) != 0) + { + return false; + } + + //Check if region is reserved. + if (BinarySearch(Reservations, Position) == null) + { + return false; + } + + //Check for overlap with already mapped buffers. + Range Map = BinarySearchLt(Maps, MapEnd); + + if (Map != null && Map.End > (ulong)Position) + { + return false; + } + + return true; + } + + public void AddMap( + long Position, + long Size, + long PhysicalAddress, + bool VaAllocated) + { + Maps.Add(Position, new MappedMemory(Position, Size, PhysicalAddress, VaAllocated)); + } + + public bool RemoveMap(long Position, out long Size) + { + Size = 0; + + if (Maps.Remove(Position, out Range Value)) + { + MappedMemory Map = (MappedMemory)Value; + + if (Map.VaAllocated) + { + Size = (long)(Map.End - Map.Start); + } + + return true; + } + + return false; + } + + public bool TryGetMapPhysicalAddress(long Position, out long PhysicalAddress) + { + Range Map = BinarySearch(Maps, Position); + + if (Map != null) + { + PhysicalAddress = ((MappedMemory)Map).PhysicalAddress; + + return true; + } + + PhysicalAddress = 0; + + return false; + } + + public void AddReservation(long Position, long Size) + { + Reservations.Add(Position, new Range(Position, Size)); + } + + public bool RemoveReservation(long Position) + { + return Reservations.Remove(Position); + } + + private Range BinarySearch(SortedList Lst, long Position) + { + int Left = 0; + int Right = Lst.Count - 1; + + while (Left <= Right) + { + int Size = Right - Left; + + int Middle = Left + (Size >> 1); + + Range Rg = Lst.Values[Middle]; + + if ((ulong)Position >= Rg.Start && (ulong)Position < Rg.End) + { + return Rg; + } + + if ((ulong)Position < Rg.Start) + { + Right = Middle - 1; + } + else + { + Left = Middle + 1; + } + } + + return null; + } + + private Range BinarySearchLt(SortedList Lst, long Position) + { + Range LtRg = null; + + int Left = 0; + int Right = Lst.Count - 1; + + while (Left <= Right) + { + int Size = Right - Left; + + int Middle = Left + (Size >> 1); + + Range Rg = Lst.Values[Middle]; + + if ((ulong)Position < Rg.Start) + { + Right = Middle - 1; + } + else + { + Left = Middle + 1; + + LtRg = Rg; + } + } + + return LtRg; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs index fcc478a41..3c67cef09 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs @@ -11,11 +11,13 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS { private const int FlagFixedOffset = 1; - private static ConcurrentDictionary Vmms; + private const int FlagRemapSubRange = 0x100; + + private static ConcurrentDictionary ASCtxs; static NvGpuASIoctl() { - Vmms = new ConcurrentDictionary(); + ASCtxs = new ConcurrentDictionary(); } public static int ProcessIoctl(ServiceCtx Context, int Cmd) @@ -29,7 +31,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS case 0x4106: return MapBufferEx (Context); case 0x4108: return GetVaRegions(Context); case 0x4109: return InitializeEx(Context); - case 0x4114: return Remap (Context); + case 0x4114: return Remap (Context, Cmd); } throw new NotImplementedException(Cmd.ToString("x8")); @@ -52,29 +54,38 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS NvGpuASAllocSpace Args = AMemoryHelper.Read(Context.Memory, InputPosition); - NvGpuVmm Vmm = GetVmm(Context); + NvGpuASCtx ASCtx = GetASCtx(Context); ulong Size = (ulong)Args.Pages * (ulong)Args.PageSize; - if ((Args.Flags & FlagFixedOffset) != 0) - { - Args.Offset = Vmm.Reserve(Args.Offset, (long)Size, 1); - } - else - { - Args.Offset = Vmm.Reserve((long)Size, 1); - } - int Result = NvResult.Success; - if (Args.Offset < 0) + lock (ASCtx) { - Args.Offset = 0; + //Note: When the fixed offset flag is not set, + //the Offset field holds the alignment size instead. + if ((Args.Flags & FlagFixedOffset) != 0) + { + Args.Offset = ASCtx.Vmm.ReserveFixed(Args.Offset, (long)Size); + } + else + { + Args.Offset = ASCtx.Vmm.Reserve((long)Size, Args.Offset); + } - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to allocate size {Size:x16}!"); + if (Args.Offset < 0) + { + Args.Offset = 0; - Result = NvResult.OutOfMemory; + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Failed to allocate size {Size:x16}!"); + + Result = NvResult.OutOfMemory; + } + else + { + ASCtx.AddReservation(Args.Offset, (long)Size); + } } AMemoryHelper.Write(Context.Memory, OutputPosition, Args); @@ -89,14 +100,29 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS NvGpuASAllocSpace Args = AMemoryHelper.Read(Context.Memory, InputPosition); - NvGpuVmm Vmm = GetVmm(Context); + NvGpuASCtx ASCtx = GetASCtx(Context); - ulong Size = (ulong)Args.Pages * - (ulong)Args.PageSize; + int Result = NvResult.Success; - Vmm.Free(Args.Offset, (long)Size); + lock (ASCtx) + { + ulong Size = (ulong)Args.Pages * + (ulong)Args.PageSize; - return NvResult.Success; + if (ASCtx.RemoveReservation(Args.Offset)) + { + ASCtx.Vmm.Free(Args.Offset, (long)Size); + } + else + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, + $"Failed to free offset 0x{Args.Offset:x16} size 0x{Size:x16}!"); + + Result = NvResult.InvalidInput; + } + } + + return Result; } private static int UnmapBuffer(ServiceCtx Context) @@ -106,11 +132,21 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS NvGpuASUnmapBuffer Args = AMemoryHelper.Read(Context.Memory, InputPosition); - NvGpuVmm Vmm = GetVmm(Context); + NvGpuASCtx ASCtx = GetASCtx(Context); - if (!Vmm.Unmap(Args.Offset)) + lock (ASCtx) { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!"); + if (ASCtx.RemoveMap(Args.Offset, out long Size)) + { + if (Size != 0) + { + ASCtx.Vmm.Free(Args.Offset, Size); + } + } + else + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!"); + } } return NvResult.Success; @@ -118,12 +154,14 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS private static int MapBufferEx(ServiceCtx Context) { + const string MapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16} and size 0x{1:x16}!"; + long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; NvGpuASMapBufferEx Args = AMemoryHelper.Read(Context.Memory, InputPosition); - NvGpuVmm Vmm = GetVmm(Context); + NvGpuASCtx ASCtx = GetASCtx(Context); NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle); @@ -134,7 +172,39 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS return NvResult.InvalidInput; } - long PA = Map.Address + Args.BufferOffset; + long PA; + + if ((Args.Flags & FlagRemapSubRange) != 0) + { + lock (ASCtx) + { + if (ASCtx.TryGetMapPhysicalAddress(Args.Offset, out PA)) + { + long VA = Args.Offset + Args.BufferOffset; + + PA += Args.BufferOffset; + + if (ASCtx.Vmm.Map(PA, VA, Args.MappingSize) < 0) + { + string Msg = string.Format(MapErrorMsg, VA, Args.MappingSize); + + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, Msg); + + return NvResult.InvalidInput; + } + + return NvResult.Success; + } + else + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Address 0x{Args.Offset:x16} not mapped!"); + + return NvResult.InvalidInput; + } + } + } + + PA = Map.Address + Args.BufferOffset; long Size = Args.MappingSize; @@ -145,40 +215,44 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS int Result = NvResult.Success; - //Note: When the fixed offset flag is not set, - //the Offset field holds the alignment size instead. - if ((Args.Flags & FlagFixedOffset) != 0) + lock (ASCtx) { - long MapEnd = Args.Offset + Args.MappingSize; + //Note: When the fixed offset flag is not set, + //the Offset field holds the alignment size instead. + bool VaAllocated = (Args.Flags & FlagFixedOffset) == 0; - if ((ulong)MapEnd <= (ulong)Args.Offset) + if (!VaAllocated) { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} and size 0x{Args.MappingSize:x16} results in a overflow!"); + if (ASCtx.ValidateFixedBuffer(Args.Offset, Size)) + { + Args.Offset = ASCtx.Vmm.Map(PA, Args.Offset, Size); + } + else + { + string Msg = string.Format(MapErrorMsg, Args.Offset, Size); - return NvResult.InvalidInput; + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, Msg); + + Result = NvResult.InvalidInput; + } } - - if ((Args.Offset & NvGpuVmm.PageMask) != 0) + else { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} is not page aligned!"); - - return NvResult.InvalidInput; + Args.Offset = ASCtx.Vmm.Map(PA, Size); } - Args.Offset = Vmm.Map(PA, Args.Offset, Size); - } - else - { - Args.Offset = Vmm.Map(PA, Size); - if (Args.Offset < 0) { Args.Offset = 0; - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to map size {Args.MappingSize:x16}!"); + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Failed to map size 0x{Size:x16}!"); Result = NvResult.InvalidInput; } + else + { + ASCtx.AddMap(Args.Offset, Size, PA, VaAllocated); + } } AMemoryHelper.Write(Context.Memory, OutputPosition, Args); @@ -206,38 +280,50 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS return NvResult.Success; } - private static int Remap(ServiceCtx Context) + private static int Remap(ServiceCtx Context, int Cmd) { + int Count = ((Cmd >> 16) & 0xff) / 0x14; + long InputPosition = Context.Request.GetBufferType0x21().Position; - NvGpuASRemap Args = AMemoryHelper.Read(Context.Memory, InputPosition); - - NvGpuVmm Vmm = GetVmm(Context); - - NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle); - - if (Map == null) + for (int Index = 0; Index < Count; Index++, InputPosition += 0x14) { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); + NvGpuASRemap Args = AMemoryHelper.Read(Context.Memory, InputPosition); - return NvResult.InvalidInput; + NvGpuVmm Vmm = GetASCtx(Context).Vmm; + + NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); + + return NvResult.InvalidInput; + } + + long Result = Vmm.Map(Map.Address, (long)(uint)Args.Offset << 16, + (long)(uint)Args.Pages << 16); + + if (Result < 0) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, + $"Page 0x{Args.Offset:x16} size 0x{Args.Pages:x16} not allocated!"); + + return NvResult.InvalidInput; + } } - //FIXME: This is most likely wrong... - Vmm.Map(Map.Address, (long)(uint)Args.Offset << 16, - (long)(uint)Args.Pages << 16); - return NvResult.Success; } - public static NvGpuVmm GetVmm(ServiceCtx Context) + public static NvGpuASCtx GetASCtx(ServiceCtx Context) { - return Vmms.GetOrAdd(Context.Process, (Key) => new NvGpuVmm(Context.Memory)); + return ASCtxs.GetOrAdd(Context.Process, (Key) => new NvGpuASCtx(Context)); } public static void UnloadProcess(Process Process) { - Vmms.TryRemove(Process, out _); + ASCtxs.TryRemove(Process, out _); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs index 411a579a2..3e030643e 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read(Context.Memory, InputPosition); - NvGpuVmm Vmm = NvGpuASIoctl.GetVmm(Context); + NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm;; for (int Index = 0; Index < Args.NumEntries; Index++) { @@ -162,7 +162,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read(Context.Memory, InputPosition); - NvGpuVmm Vmm = NvGpuASIoctl.GetVmm(Context); + NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm;; for (int Index = 0; Index < Args.NumEntries; Index++) { diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs index 7705a1f78..7f2034538 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs @@ -120,7 +120,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostCtrl Context.Ns.Log.PrintDebug(Logging.LogClass.ServiceNv, $"Got setting {Domain}!{Name}"); } - + return NvResult.Success; } diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapFree.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapFree.cs index 1e13f1974..bc66fc717 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapFree.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapFree.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap { public int Handle; public int Padding; - public long RefCount; + public long Address; public int Size; public int Flags; } diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapHandle.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapHandle.cs index 21fce700c..f349a03ef 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapHandle.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapHandle.cs @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap public long DecrementRefCount() { - return Interlocked.Decrement(ref Dupes) + 1; + return Interlocked.Decrement(ref Dupes); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs index 4c6107f8f..e5b298259 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs @@ -129,7 +129,8 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap { //When the address is zero, we need to allocate //our own backing memory for the NvMap. - if (!Context.Ns.Os.Allocator.TryAllocate((uint)Size, out Address)) + //TODO: Is this allocation inside the transfer memory? + if (!Context.Ns.Memory.Allocator.TryAllocate((uint)Size, out Address)) { Result = NvResult.OutOfMemory; } @@ -163,23 +164,22 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap return NvResult.InvalidInput; } - long OldRefCount = Map.DecrementRefCount(); - - if (OldRefCount <= 1) + if (Map.DecrementRefCount() <= 0) { DeleteNvMap(Context, Args.Handle); Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!"); - Args.Flags = 0; + Args.Address = Map.Address; + Args.Flags = 0; } else { - Args.Flags = FlagNotFreedYet; + Args.Address = 0; + Args.Flags = FlagNotFreedYet; } - Args.RefCount = OldRefCount; - Args.Size = Map.Size; + Args.Size = Map.Size; AMemoryHelper.Write(Context.Memory, OutputPosition, Args); diff --git a/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs b/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs index b8447ac65..4788c5aff 100644 --- a/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs @@ -1,4 +1,4 @@ -using Ryujinx.HLE.Font; +using Ryujinx.HLE.OsHle.Font; using Ryujinx.HLE.OsHle.Ipc; using System.Collections.Generic; @@ -27,8 +27,8 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); - Context.Ns.Font.Load(FontType); - + //We don't need to do anything here because we do lazy initialization + //on SharedFontManager (the font is loaded when necessary). return 0; } @@ -36,7 +36,9 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); - Context.ResponseData.Write(Context.Ns.Font.GetLoadState(FontType)); + //1 (true) indicates that the font is already loaded. + //All fonts are already loaded. + Context.ResponseData.Write(1); return 0; } @@ -45,7 +47,7 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); - Context.ResponseData.Write(Context.Ns.Font.GetFontSize(FontType)); + Context.ResponseData.Write(Context.Ns.Os.Font.GetFontSize(FontType)); return 0; } @@ -54,13 +56,15 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); - Context.ResponseData.Write(Context.Ns.Font.GetSharedMemoryAddressOffset(FontType)); + Context.ResponseData.Write(Context.Ns.Os.Font.GetSharedMemoryAddressOffset(FontType)); return 0; } public long GetSharedMemoryNativeHandle(ServiceCtx Context) { + Context.Ns.Os.Font.EnsureInitialized(); + int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.FontSharedMem); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -68,50 +72,54 @@ namespace Ryujinx.HLE.OsHle.Services.Pl return 0; } - private uint AddFontToOrderOfPriorityList(ServiceCtx Context, SharedFontType FontType, uint BufferPos, out uint LoadState) + public long GetSharedFontInOrderOfPriority(ServiceCtx Context) { - long TypesPosition = Context.Request.ReceiveBuff[0].Position; - long TypesSize = Context.Request.ReceiveBuff[0].Size; + long LanguageCode = Context.RequestData.ReadInt64(); + int LoadedCount = 0; - long OffsetsPosition = Context.Request.ReceiveBuff[1].Position; - long OffsetsSize = Context.Request.ReceiveBuff[1].Size; + for (SharedFontType Type = 0; Type < SharedFontType.Count; Type++) + { + int Offset = (int)Type * 4; + + if (!AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, Offset)) + { + break; + } + + LoadedCount++; + } + + Context.ResponseData.Write(LoadedCount); + Context.ResponseData.Write((int)SharedFontType.Count); + + return 0; + } + + private bool AddFontToOrderOfPriorityList(ServiceCtx Context, SharedFontType FontType, int Offset) + { + long TypesPosition = Context.Request.ReceiveBuff[0].Position; + long TypesSize = Context.Request.ReceiveBuff[0].Size; + + long OffsetsPosition = Context.Request.ReceiveBuff[1].Position; + long OffsetsSize = Context.Request.ReceiveBuff[1].Size; long FontSizeBufferPosition = Context.Request.ReceiveBuff[2].Position; long FontSizeBufferSize = Context.Request.ReceiveBuff[2].Size; - LoadState = Context.Ns.Font.GetLoadState(FontType); - - if (BufferPos >= TypesSize || BufferPos >= OffsetsSize || BufferPos >= FontSizeBufferSize) + if ((uint)Offset + 4 > (uint)TypesSize || + (uint)Offset + 4 > (uint)OffsetsSize || + (uint)Offset + 4 > (uint)FontSizeBufferSize) { - return 0; + return false; } - Context.Memory.WriteUInt32(TypesPosition + BufferPos, (uint)FontType); - Context.Memory.WriteUInt32(OffsetsPosition + BufferPos, Context.Ns.Font.GetSharedMemoryAddressOffset(FontType)); - Context.Memory.WriteUInt32(FontSizeBufferPosition + BufferPos, Context.Ns.Font.GetFontSize(FontType)); + Context.Memory.WriteInt32(TypesPosition + Offset, (int)FontType); - BufferPos += 4; + Context.Memory.WriteInt32(OffsetsPosition + Offset, Context.Ns.Os.Font.GetSharedMemoryAddressOffset(FontType)); - return BufferPos; - } + Context.Memory.WriteInt32(FontSizeBufferPosition + Offset, Context.Ns.Os.Font.GetFontSize(FontType)); - public long GetSharedFontInOrderOfPriority(ServiceCtx Context) - { - ulong LanguageCode = Context.RequestData.ReadUInt64(); - uint LoadedCount = 0; - uint BufferPos = 0; - uint Loaded = 0; - - for (int Type = 0; Type < Context.Ns.Font.Count; Type++) - { - BufferPos = AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, BufferPos, out Loaded); - LoadedCount += Loaded; - } - - Context.ResponseData.Write(LoadedCount); - Context.ResponseData.Write(Context.Ns.Font.Count); - - return 0; + return true; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs index 41f2916f4..04b50bb35 100644 --- a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs @@ -159,7 +159,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android int Slot = GetFreeSlotBlocking(Width, Height); - return MakeReplyParcel(Context, Slot, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + return MakeReplyParcel(Context, Slot, 1, 0x24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader) diff --git a/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs b/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs index 39cced280..010dbb20d 100644 --- a/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs +++ b/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs @@ -11,5 +11,15 @@ namespace Ryujinx.HLE.OsHle.Utilities { return (Value + (Size - 1)) & ~((long)Size - 1); } + + public static int AlignDown(int Value, int Size) + { + return Value & ~(Size - 1); + } + + public static long AlignDown(long Value, int Size) + { + return Value & ~((long)Size - 1); + } } } diff --git a/Ryujinx.HLE/PerformanceStatistics.cs b/Ryujinx.HLE/PerformanceStatistics.cs index 9bc3d6b47..2dabd9a08 100644 --- a/Ryujinx.HLE/PerformanceStatistics.cs +++ b/Ryujinx.HLE/PerformanceStatistics.cs @@ -1,7 +1,6 @@ using System.Diagnostics; using System.Timers; - namespace Ryujinx.HLE { public class PerformanceStatistics diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 475e51160..165922e90 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -1,9 +1,9 @@ using Ryujinx.Audio; using Ryujinx.Graphics.Gal; -using Ryujinx.HLE.Font; using Ryujinx.HLE.Gpu; using Ryujinx.HLE.Input; using Ryujinx.HLE.Logging; +using Ryujinx.HLE.Memory; using Ryujinx.HLE.OsHle; using System; @@ -15,6 +15,8 @@ namespace Ryujinx.HLE public Logger Log { get; private set; } + internal DeviceMemory Memory { get; private set; } + internal NvGpu Gpu { get; private set; } internal VirtualFileSystem VFs { get; private set; } @@ -25,8 +27,6 @@ namespace Ryujinx.HLE public Hid Hid { get; private set; } - public SharedFontManager Font { get; private set; } - public event EventHandler Finish; public Switch(IGalRenderer Renderer, IAalOutput AudioOut) @@ -45,6 +45,8 @@ namespace Ryujinx.HLE Log = new Logger(); + Memory = new DeviceMemory(); + Gpu = new NvGpu(Renderer); VFs = new VirtualFileSystem(); @@ -53,15 +55,7 @@ namespace Ryujinx.HLE Statistics = new PerformanceStatistics(); - Hid = new Hid(Log); - - Font = new SharedFontManager(Log, VFs.GetSystemPath()); - - Os.HidSharedMem.MemoryMapped += Hid.ShMemMap; - Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap; - - Os.FontSharedMem.MemoryMapped += Font.ShMemMap; - Os.FontSharedMem.MemoryUnmapped += Font.ShMemUnmap; + Hid = new Hid(this, Os.HidSharedMem.PA); } public void LoadCart(string ExeFsDir, string RomFsFile = null) diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 70a8e1924..e6a023798 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -5,6 +5,7 @@ using ChocolArm64.State; using NUnit.Framework; using System; +using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; using System.Threading; @@ -19,6 +20,8 @@ namespace Ryujinx.Tests.Cpu private long EntryPoint; + private IntPtr RamPointer; + private AMemory Memory; private AThread Thread; @@ -31,15 +34,16 @@ namespace Ryujinx.Tests.Cpu EntryPoint = Position; ATranslator Translator = new ATranslator(); - Memory = new AMemory(); - Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute); + RamPointer = Marshal.AllocHGlobal(new IntPtr(Size)); + Memory = new AMemory(RamPointer); + Memory.Map(Position, 0, Size); Thread = new AThread(Translator, Memory, EntryPoint); } [TearDown] public void Teardown() { - Memory.Dispose(); + Marshal.FreeHGlobal(RamPointer); Memory = null; Thread = null; } @@ -52,7 +56,7 @@ namespace Ryujinx.Tests.Cpu protected void Opcode(uint Opcode) { - Thread.Memory.WriteUInt32Unchecked(Position, Opcode); + Thread.Memory.WriteUInt32(Position, Opcode); Position += 4; } diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index 145427068..ec0f05b79 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -23,8 +23,6 @@ namespace Ryujinx IniParser Parser = new IniParser(IniPath); - AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks")); - GraphicsConfig.ShadersDumpPath = Parser.Value("Graphics_Shaders_Dump_Path"); Device.Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug")));