aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-08-15 15:59:51 -0300
committerGitHub <noreply@github.com>2018-08-15 15:59:51 -0300
commitc393cdf8e3775bc95850e4d8c8e4c446b286d3b4 (patch)
tree25035a244741d2daf3f7d6be8b23153ff061ea15
parent76d95dee05e3c51c18e1799f54cc407e0f633b4e (diff)
More flexible memory manager (#307)
* Keep track mapped buffers with fixed offsets * Started rewriting the memory manager * Initial support for MapPhysicalMemory and UnmapPhysicalMemory, other tweaks * MapPhysicalMemory/UnmapPhysicalMemory support, other tweaks * Rebased * Optimize the map/unmap physical memory svcs * Integrate shared font support * Fix address space reserve alignment * Some fixes related to gpu memory mapping * Some cleanup * Only try uploading const buffers that are really used * Check if memory region is contiguous * Rebased * Add missing count increment on IsRegionModified * Check for reads/writes outside of the address space, optimize translation with a tail call
-rw-r--r--ChocolArm64/AOptimizations.cs2
-rw-r--r--ChocolArm64/Exceptions/VmmAccessViolationException.cs14
-rw-r--r--ChocolArm64/Exceptions/VmmOutOfMemoryException.cs8
-rw-r--r--ChocolArm64/Instruction/AInstEmitMemoryHelper.cs86
-rw-r--r--ChocolArm64/Memory/AMemory.cs588
-rw-r--r--ChocolArm64/Memory/AMemoryHelper.cs24
-rw-r--r--ChocolArm64/Memory/AMemoryMapInfo.cs21
-rw-r--r--ChocolArm64/Memory/AMemoryMgr.cs258
-rw-r--r--ChocolArm64/Memory/AMemoryPerm.cs15
-rw-r--r--ChocolArm64/Memory/AMemoryWin32.cs92
-rw-r--r--Ryujinx.Graphics/Gal/IGalShader.cs1
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs2
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs16
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs21
-rw-r--r--Ryujinx.HLE/Font/SharedFontManager.cs177
-rw-r--r--Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs35
-rw-r--r--Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs47
-rw-r--r--Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs14
-rw-r--r--Ryujinx.HLE/Gpu/Texture/TextureReader.cs22
-rw-r--r--Ryujinx.HLE/Gpu/Texture/TextureWriter.cs2
-rw-r--r--Ryujinx.HLE/Hid/Hid.cs204
-rw-r--r--Ryujinx.HLE/Loaders/Executable.cs67
-rw-r--r--Ryujinx.HLE/Memory/ArenaAllocator.cs112
-rw-r--r--Ryujinx.HLE/Memory/DeviceMemory.cs130
-rw-r--r--Ryujinx.HLE/OsHle/Font/SharedFontManager.cs122
-rw-r--r--Ryujinx.HLE/OsHle/Font/SharedFontType.cs (renamed from Ryujinx.HLE/Font/SharedFontType.cs)5
-rw-r--r--Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs10
-rw-r--r--Ryujinx.HLE/OsHle/Handles/HSharedMem.cs44
-rw-r--r--Ryujinx.HLE/OsHle/Handles/HTransferMem.cs21
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs43
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs33
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs1082
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs14
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs60
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs14
-rw-r--r--Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs22
-rw-r--r--Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs18
-rw-r--r--Ryujinx.HLE/OsHle/Handles/MemoryState.cs49
-rw-r--r--Ryujinx.HLE/OsHle/Homebrew.cs16
-rw-r--r--Ryujinx.HLE/OsHle/Horizon.cs61
-rw-r--r--Ryujinx.HLE/OsHle/Kernel/KernelErr.cs29
-rw-r--r--Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs29
-rw-r--r--Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs527
-rw-r--r--Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs41
-rw-r--r--Ryujinx.HLE/OsHle/Kernel/SvcThread.cs4
-rw-r--r--Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs16
-rw-r--r--Ryujinx.HLE/OsHle/MemoryAllocator.cs12
-rw-r--r--Ryujinx.HLE/OsHle/MemoryRegions.cs29
-rw-r--r--Ryujinx.HLE/OsHle/Process.cs135
-rw-r--r--Ryujinx.HLE/OsHle/Services/Hid/IAppletResource.cs4
-rw-r--r--Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASCtx.cs198
-rw-r--r--Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs210
-rw-r--r--Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs4
-rw-r--r--Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs2
-rw-r--r--Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapFree.cs2
-rw-r--r--Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapHandle.cs2
-rw-r--r--Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs16
-rw-r--r--Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs78
-rw-r--r--Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs2
-rw-r--r--Ryujinx.HLE/OsHle/Utilities/IntUtils.cs10
-rw-r--r--Ryujinx.HLE/PerformanceStatistics.cs1
-rw-r--r--Ryujinx.HLE/Switch.cs18
-rw-r--r--Ryujinx.Tests/Cpu/CpuTest.cs12
-rw-r--r--Ryujinx/Config.cs2
64 files changed, 3196 insertions, 1759 deletions
diff --git a/ChocolArm64/AOptimizations.cs b/ChocolArm64/AOptimizations.cs
index 800cf363..fbf26a49 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 a557502e..00000000
--- 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 c11384da..4a03b65c 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 df091bd5..b10551fe 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 e969cca5..1b4ff6fb 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;
+
+ 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;
- public AMemoryMgr Manager { get; private set; }
+ 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<int, ArmMonitor> Monitors;
+ private ConcurrentDictionary<long, IntPtr> 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<int, ArmMonitor>();
- IntPtr Size = (IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize;
+ ObservedPages = new ConcurrentDictionary<long, IntPtr>();
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- Ram = AMemoryWin32.Allocate(Size);
+ this.Ram = Ram;
- HostPageSize = AMemoryWin32.GetPageSize(Ram, Size);
- }
- else
+ RamPtr = (byte*)Ram;
+
+ PageTable = (byte***)Marshal.AllocHGlobal(PTLvl0Size * IntPtr.Size);
+
+ for (int L0 = 0; L0 < PTLvl0Size; L0++)
{
- Ram = Marshal.AllocHGlobal(Size);
+ PageTable[L0] = null;
}
-
- RamPtr = (byte*)Ram;
}
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<float> ReadVector8(long Position)
@@ -274,6 +219,7 @@ namespace ChocolArm64.Memory
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector128<float> ReadVector16(long Position)
{
if (Sse2.IsSupported)
@@ -286,122 +232,12 @@ namespace ChocolArm64.Memory
}
}
- public Vector128<float> ReadVector32(long Position)
- {
- EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
- EnsureAccessIsValid(Position + 3, AMemoryPerm.Read);
-
- if (Sse.IsSupported)
- {
- return Sse.LoadScalarVector128((float*)(RamPtr + (uint)Position));
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- public Vector128<float> ReadVector64(long Position)
- {
- EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
- EnsureAccessIsValid(Position + 7, AMemoryPerm.Read);
-
- if (Sse2.IsSupported)
- {
- return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)(RamPtr + (uint)Position)));
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- public Vector128<float> 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<float> ReadVector8Unchecked(long Position)
- {
- if (Sse2.IsSupported)
- {
- return Sse.StaticCast<byte, float>(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<float> ReadVector16Unchecked(long Position)
- {
- if (Sse2.IsSupported)
- {
- return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse2.SetZeroVector128<ushort>(), ReadUInt16Unchecked(Position), 0));
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
[MethodImpl(MethodImplOptions.NoInlining)]
- public Vector128<float> ReadVector32Unchecked(long Position)
+ public Vector128<float> ReadVector32(long Position)
{
if (Sse.IsSupported)
{
- return Sse.LoadScalarVector128((float*)(RamPtr + (uint)Position));
+ return Sse.LoadScalarVector128((float*)Translate(Position));
}
else
{
@@ -410,11 +246,11 @@ namespace ChocolArm64.Memory
}
[MethodImpl(MethodImplOptions.NoInlining)]
- public Vector128<float> ReadVector64Unchecked(long Position)
+ public Vector128<float> ReadVector64(long Position)
{
if (Sse2.IsSupported)
{
- return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)(RamPtr + (uint)Position)));
+ return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)Translate(Position)));
}
else
{
@@ -423,11 +259,11 @@ namespace ChocolArm64.Memory
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Vector128<float> ReadVector128Unchecked(long Position)
+ public Vector128<float> ReadVector128(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<float> Value)
{
if (Sse41.IsSupported)
@@ -518,6 +344,7 @@ namespace ChocolArm64.Memory
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector16(long Position, Vector128<float> Value)
{
if (Sse2.IsSupported)
@@ -530,14 +357,12 @@ namespace ChocolArm64.Memory
}
}
+ [MethodImpl(MethodImplOptions.NoInlining)]
public void WriteVector32(long Position, Vector128<float> 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<float> Value)
{
- EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
- EnsureAccessIsValid(Position + 7, AMemoryPerm.Write);
-
if (Sse2.IsSupported)
{
- Sse2.StoreScalar((double*)(RamPtr + (uint)Position), Sse.StaticCast<float, double>(Value));
+ Sse2.StoreScalar((double*)TranslateWrite(Position), Sse.StaticCast<float, double>(Value));
}
else
{
@@ -560,14 +383,12 @@ namespace ChocolArm64.Memory
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector128(long Position, Vector128<float> Value)
{
- EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
- EnsureAccessIsValid(Position + 15, AMemoryPerm.Write);
-
if (Sse.IsSupported)
{
- Sse.Store((float*)(RamPtr + (uint)Position), Value);
+ Sse.Store((float*)TranslateWrite(Position), Value);
}
else
{
@@ -575,147 +396,287 @@ namespace ChocolArm64.Memory
}
}
- public void WriteSByteUnchecked(long Position, sbyte Value)
+ public void WriteBytes(long Position, byte[] Data)
{
- WriteByteUnchecked(Position, (byte)Value);
- }
+ EnsureRangeIsValid(Position, (uint)Data.Length);
- public void WriteInt16Unchecked(long Position, short Value)
- {
- WriteUInt16Unchecked(Position, (ushort)Value);
+ Marshal.Copy(Data, 0, (IntPtr)TranslateWrite(Position), Data.Length);
}
- public void WriteInt32Unchecked(long Position, int Value)
+ public void Map(long VA, long PA, long Size)
{
- WriteUInt32Unchecked(Position, (uint)Value);
+ SetPTEntries(VA, RamPtr + PA, Size);
}
- public void WriteInt64Unchecked(long Position, long Value)
+ public void Unmap(long Position, long Size)
{
- WriteUInt64Unchecked(Position, (ulong)Value);
- }
+ SetPTEntries(Position, null, Size);
- public void WriteByteUnchecked(long Position, byte Value)
- {
- *((byte*)(RamPtr + (uint)Position)) = Value;
+ StopObservingRegion(Position, Size);
}
- public void WriteUInt16Unchecked(long Position, ushort Value)
+ public bool IsMapped(long Position)
{
- *((ushort*)(RamPtr + (uint)Position)) = Value;
- }
+ if (!(IsValidPosition(Position)))
+ {
+ return false;
+ }
- public void WriteUInt32Unchecked(long Position, uint Value)
- {
- *((uint*)(RamPtr + (uint)Position)) = Value;
+ 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 void WriteUInt64Unchecked(long Position, ulong Value)
+ public long GetPhysicalAddress(long VirtualAddress)
{
- *((ulong*)(RamPtr + (uint)Position)) = Value;
+ byte* Ptr = Translate(VirtualAddress);
+
+ return (long)(Ptr - RamPtr);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteVector8Unchecked(long Position, Vector128<float> Value)
+ internal byte* Translate(long Position)
{
- if (Sse41.IsSupported)
+ long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
+ long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
+
+ long Old = Position;
+
+ byte** Lvl1 = PageTable[L0];
+
+ if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0)
{
- WriteByteUnchecked(Position, Sse41.Extract(Sse.StaticCast<float, byte>(Value), 0));
+ goto Unmapped;
}
- else if (Sse2.IsSupported)
+
+ if (Lvl1 == null)
{
- WriteByteUnchecked(Position, (byte)Sse2.Extract(Sse.StaticCast<float, ushort>(Value), 0));
+ goto Unmapped;
}
- else
+
+ Position &= PageMask;
+
+ byte* Ptr = Lvl1[L1];
+
+ if (Ptr == null)
{
- throw new PlatformNotSupportedException();
+ goto Unmapped;
}
+
+ return Ptr + Position;
+
+Unmapped:
+ return HandleNullPte(Old);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteVector16Unchecked(long Position, Vector128<float> Value)
+ private byte* HandleNullPte(long Position)
{
- if (Sse2.IsSupported)
+ long Key = Position >> PTPageBits;
+
+ if (ObservedPages.TryGetValue(Key, out IntPtr Ptr))
{
- WriteUInt16Unchecked(Position, Sse2.Extract(Sse.StaticCast<float, ushort>(Value), 0));
+ return (byte*)Ptr + (Position & PageMask);
}
- else
+
+ 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)
{
- throw new PlatformNotSupportedException();
+ 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);
}
- [MethodImpl(MethodImplOptions.NoInlining)]
- public void WriteVector32Unchecked(long Position, Vector128<float> Value)
+ private byte* HandleNullPteWrite(long Position)
{
- if (Sse.IsSupported)
+ long Key = Position >> PTPageBits;
+
+ if (ObservedPages.TryGetValue(Key, out IntPtr Ptr))
{
- Sse.StoreScalar((float*)(RamPtr + (uint)Position), Value);
+ SetPTEntry(Position, (byte*)Ptr);
+
+ return (byte*)Ptr + (Position & PageMask);
}
- else
+
+ throw new VmmPageFaultException(Position);
+ }
+
+ private void SetPTEntries(long VA, byte* Ptr, long Size)
+ {
+ long EndPosition = (VA + Size + PageMask) & ~PageMask;
+
+ while ((ulong)VA < (ulong)EndPosition)
{
- throw new PlatformNotSupportedException();
+ SetPTEntry(VA, Ptr);
+
+ VA += PageSize;
+
+ if (Ptr != null)
+ {
+ Ptr += PageSize;
+ }
}
}
- [MethodImpl(MethodImplOptions.NoInlining)]
- public void WriteVector64Unchecked(long Position, Vector128<float> Value)
+ private void SetPTEntry(long Position, byte* Ptr)
{
- if (Sse2.IsSupported)
+ if (!IsValidPosition(Position))
{
- Sse2.StoreScalar((double*)(RamPtr + (uint)Position), Sse.StaticCast<float, double>(Value));
+ throw new ArgumentOutOfRangeException(nameof(Position));
}
- else
+
+ long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
+ long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
+
+ if (PageTable[L0] == null)
{
- throw new PlatformNotSupportedException();
+ 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;
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteVector128Unchecked(long Position, Vector128<float> Value)
+ public (bool[], int) IsRegionModified(long Position, long Size)
{
- if (Sse.IsSupported)
+ long EndPosition = (Position + Size + PageMask) & ~PageMask;
+
+ Position &= ~PageMask;
+
+ Size = EndPosition - Position;
+
+ bool[] Modified = new bool[Size >> PTPageBits];
+
+ int Count = 0;
+
+ lock (ObservedPages)
{
- Sse.Store((float*)(RamPtr + (uint)Position), Value);
+ 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;
+ }
}
- else
+
+ return (Modified, Count);
+ }
+
+ public void StopObservingRegion(long Position, long Size)
+ {
+ long EndPosition = (Position + Size + PageMask) & ~PageMask;
+
+ while (Position < EndPosition)
{
- throw new PlatformNotSupportedException();
+ lock (ObservedPages)
+ {
+ if (ObservedPages.TryRemove(Position >> PTPageBits, out IntPtr Ptr))
+ {
+ SetPTEntry(Position, (byte*)Ptr);
+ }
+ }
+
+ Position += PageSize;
}
}
- public void WriteBytes(long Position, byte[] Data)
+ public IntPtr GetHostAddress(long Position, long Size)
{
- EnsureRangeIsValid(Position, (uint)Data.Length, AMemoryPerm.Write);
+ EnsureRangeIsValid(Position, Size);
- Marshal.Copy(Data, 0, (IntPtr)(RamPtr + (uint)Position), Data.Length);
+ return (IntPtr)Translate(Position);
}
- private void EnsureRangeIsValid(long Position, long Size, AMemoryPerm Perm)
+ 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);
+
+ if (PA != ExpectedPA)
+ {
+ throw new VmmAccessException(Position, Size);
+ }
- Position += AMemoryMgr.PageSize;
+ 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))
- {
- AMemoryWin32.Free(Ram);
- }
- else
+ return;
+ }
+
+ for (int L0 = 0; L0 < PTLvl0Size; L0++)
+ {
+ if (PageTable[L0] != null)
{
- 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 0a23a2f8..ea877834 100644
--- a/ChocolArm64/Memory/AMemoryHelper.cs
+++ b/ChocolArm64/Memory/AMemoryHelper.cs
@@ -26,12 +26,9 @@ namespace ChocolArm64.Memory
{
long Size = Marshal.SizeOf<T>();
- 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<T>(Ptr);
}
@@ -40,12 +37,9 @@ namespace ChocolArm64.Memory
{
long Size = Marshal.SizeOf<T>();
- 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<T>(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 02dd3055..00000000
--- 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 8a165b07..00000000
--- 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 b425eb94..00000000
--- 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 387ca32c..00000000
--- 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 a9bd1381..5174c039 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<ShaderDeclInfo> GetConstBufferUsage(long Key);
IEnumerable<ShaderDeclInfo> 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 54f984cd..c9e7f6c3 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 4792bc5e..9c7b8668 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<ShaderDeclInfo> GetConstBufferUsage(long Key)
+ {
+ if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
+ {
+ return Stage.ConstBufferUsage;
+ }
+
+ return Enumerable.Empty<ShaderDeclInfo>();
}
public IEnumerable<ShaderDeclInfo> 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 731994ce..c4e6a881 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<ShaderDeclInfo> TextureUsage { get; private set; }
- public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; }
+ public IEnumerable<ShaderDeclInfo> ConstBufferUsage { get; private set; }
+ public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
public OGLShaderStage(
- GalShaderType Type,
- string Code,
- IEnumerable<ShaderDeclInfo> TextureUsage,
- IEnumerable<ShaderDeclInfo> UniformUsage)
+ GalShaderType Type,
+ string Code,
+ IEnumerable<ShaderDeclInfo> ConstBufferUsage,
+ IEnumerable<ShaderDeclInfo> 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 fce270de..00000000
--- 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<SharedFontType, byte[]> 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, byte[]>()
- {
- { 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 cb1514b5..6f601244 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)
+ {
+ continue;
+ }
- if (Cb.Enabled && QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
+ long Key = Vmm.GetPhysicalAddress(Cb.Position);
+
+ if (QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
{
- IntPtr Source = Vmm.GetHostAddress(Key, Cb.Size);
+ 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 7b23e49f..e7e18064 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<long, MappedMemory> Maps;
-
private NvGpuVmmCache Cache;
private const long PteUnmapped = -1;
@@ -49,8 +36,6 @@ namespace Ryujinx.HLE.Gpu.Memory
{
this.Memory = Memory;
- Maps = new ConcurrentDictionary<long, MappedMemory>();
-
Cache = new NvGpuVmmCache();
PageTable = new long[PTLvl0Size][];
@@ -62,14 +47,6 @@ namespace Ryujinx.HLE.Gpu.Memory
{
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 3c256044..b3f253b3 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<long>[AMemoryMgr.RamSize / PageSize];
+ Residency = new HashSet<long>[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 f8cd6765..0c6103af 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 a87d4545..113dc6f6 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 43c040bb..492eb30a 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;
-
- ShMemLock = new object();
-
- ShMemPositions = new (AMemory, long, long)[0];
- }
+ this.Device = Device;
+ this.HidPosition = HidPosition;
- 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);
- }
- }
+ Device.Memory.FillWithZeros(HidPosition, Horizon.HidSize);
- 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)
- {
- foreach ((AMemory Memory, long Position, long Size) in ShMemPositions)
- {
- long TouchScreenOffset = Position + HidTouchScreenOffset;
-
- long LastEntry = Memory.ReadInt64Unchecked(TouchScreenOffset + 0x10);
+ long TouchScreenOffset = HidPosition + HidTouchScreenOffset;
- long CurrEntry = (LastEntry + 1) % HidEntryCount;
+ long LastEntry = Device.Memory.ReadInt64(TouchScreenOffset + 0x10);
- long Timestamp = GetTimestamp();
+ long CurrEntry = (LastEntry + 1) % HidEntryCount;
- 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 Timestamp = GetTimestamp();
- long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize;
+ 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 LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize;
+ long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize;
- long SampleCounter = Memory.ReadInt64Unchecked(LastEntryOffset) + 1;
+ long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize;
- TouchEntryOffset += CurrEntry * HidTouchEntrySize;
+ long SampleCounter = Device.Memory.ReadInt64(LastEntryOffset) + 1;
- Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, SampleCounter);
- Memory.WriteInt64Unchecked(TouchEntryOffset + 0x8, Points.Length);
+ TouchEntryOffset += CurrEntry * HidTouchEntrySize;
- TouchEntryOffset += HidTouchEntryHeaderSize;
+ Device.Memory.WriteInt64(TouchEntryOffset + 0x0, SampleCounter);
+ Device.Memory.WriteInt64(TouchEntryOffset + 0x8, Points.Length);
- const int Padding = 0;
+ TouchEntryOffset += HidTouchEntryHeaderSize;
- int Index = 0;
+ const int Padding = 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);
+ int Index = 0;
- TouchEntryOffset += HidTouchEntryTouchSize;
- }
- }
+ foreach (HidTouchPoint Point in Points)
+ {
+ 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);
+
+ TouchEntryOffset += HidTouchEntryTouchSize;
}
}
diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs
index 84f5ff39..ba5f8d7e 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<ElfDyn>();
@@ -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;
- if (Exe.Mod0Offset == 0)
- {
- int BssOffset = Exe.DataOffset + Exe.Data.Length;
- int BssSize = Exe.BssSize;
+ 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;
- MapBss(ImageBase + BssOffset, BssSize);
+ MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize);
- ImageEnd = ImageBase + BssOffset + BssSize;
+ 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)
+ {
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 00000000..5e15f46a
--- /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<Region> FreeRegions;
+
+ public long TotalAvailableSize { get; private set; }
+ public long TotalUsedSize { get; private set; }
+
+ public ArenaAllocator(long ArenaSize)
+ {
+ TotalAvailableSize = ArenaSize;
+
+ FreeRegions = new LinkedList<Region>();
+
+ FreeRegions.AddFirst(new Region(0, ArenaSize));
+ }
+
+ public bool TryAllocate(long Size, out long Position)
+ {
+ LinkedListNode<Region> 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<Region> Node = FreeRegions.First;
+ LinkedListNode<Region> PrevSz = Node;
+
+ while (Node != null)
+ {
+ LinkedListNode<Region> 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 00000000..3c5f2e5f
--- /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 00000000..12b6973e
--- /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<SharedFontType, FontInfo> 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, FontInfo>()
+ {
+ { 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
index ca8a42b0..80f42a7d 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 00000000..946c51e1
--- /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 cd3d8223..00000000
--- 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<EventArgs> MemoryMapped;
- public EventHandler<EventArgs> 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 2969a2ba..00000000
--- 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 00000000..e33b6cb9
--- /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 00000000..3f79f000
--- /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 00000000..76c5a53d
--- /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<KMemoryBlock> 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<KMemoryBlock>();
+
+ 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<KMemoryBlock> BaseNode = FindBlockNode(Position);
+
+ LinkedListNode<KMemoryBlock> 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<KMemoryBlock> 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<KMemoryBlock> Node = Blocks.First;
+
+ while (Node != null)
+ {
+ LinkedListNode<KMemoryBlock> NewNode = Node;
+ LinkedListNode<KMemoryBlock> 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<KMemoryBlock> NewNode = null;
+
+ LinkedListNode<KMemoryBlock> Node = Blocks.First;
+
+ while (Node != null)
+ {
+ KMemoryBlock CurrBlock = Node.Value;
+
+ LinkedListNode<KMemoryBlock> 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<KMemoryBlock> 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<KMemoryBlock> FindBlockNode(long Position)
+ {
+ ulong Addr = (ulong)Position;
+
+ lock (Blocks)
+ {
+ LinkedListNode<KMemoryBlock> 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 00000000..5c410474
--- /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 00000000..f116f548
--- /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 00000000..5f9c0409
--- /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 00000000..e234d7f0
--- /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 00000000..416f171e
--- /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 00000000..5437d6a3
--- /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 778e52fe..90c6c4c6 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 b4c7d36e..1fd210dd 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 KSharedMemory(HidPA, HidSize);
+ FontSharedMem = new KSharedMemory(FontPA, FontSize);
- HidSharedMem = new HSharedMem();
- FontSharedMem = new HSharedMem();
+ 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 bbae5325..a62fc1bf 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 6f7bc42f..a33ffe5e 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<KThread, AutoResetEvent> 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<KThread, AutoResetEvent>();
-
- MappedSharedMems = new HashSet<(HSharedMem, long, long)>();
}
static SvcHandler()
@@ -126,26 +120,5 @@ namespace Ryujinx.HLE.OsHle.Kernel
return Process.HandleTable.GetData<KThread>(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 f10cad7a..68d87293 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 & 0x1fffff) != 0 || Size != (uint)Size)
+ {
+ 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);
- if (Size > CurrentHeapSize)
+ ThreadState.X0 = (ulong)Result;
+
+ if (Result == 0)
{
- Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW);
+ 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);
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+
+ return;
}
- else if (State0 == 8 && State1 == 8)
+
+ if (!PageAligned(Size) || Size == 0)
{
- Memory.Manager.SetAttrBit(Position, Size, 3);
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
+
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
+
+ return;
}
- ThreadState.X0 = 0;
+ 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.InvalidMemRange);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
return;
}
- if (!IsValidMapPosition(Dst))
+ if (!PageAligned(Size) || Size == 0)
{
- Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!");
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
return;
}
- AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src);
+ if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
+ {
+ Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
- Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
- Memory.Manager.Reprotect(Src, Size, AMemoryPerm.None);
+ return;
+ }
+
+ if (!InsideAddrSpace(Src, Size))
+ {
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
- Memory.Manager.SetAttrBit(Src, Size, 0);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
- ThreadState.X0 = 0;
+ 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;
+ }
+
+ long Result = Process.MemoryManager.Map(Src, Dst, Size);
+
+ if (Result != 0)
+ {
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ }
+
+ 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.InvalidMemRange);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
return;
}
- if (!IsValidMapPosition(Dst))
+ if (!PageAligned(Size) || Size == 0)
{
- Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!");
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+ 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;
}
- AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst);
+ if (!InsideAddrSpace(Src, Size))
+ {
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
- Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
- Memory.Manager.Reprotect(Src, Size, DstInfo.Perm);
+ return;
+ }
- Memory.Manager.ClearAttrBit(Src, Size, 0);
+ if (!InsideNewMapRegion(Dst, Size))
+ {
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
- ThreadState.X0 = 0;
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+
+ return;
+ }
+
+ long Result = Process.MemoryManager.Unmap(Src, Dst, Size);
+
+ if (Result != 0)
+ {
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ }
+
+ 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);
-
- if (MapInfo == null)
- {
- long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
-
- long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1;
+ KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position);
- 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;
+ }
+
+ 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;
}
- HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
+ MemoryPermission Permission = (MemoryPermission)ThreadState.X3;
- if (SharedMem != null)
+ if ((Permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite)
{
- Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write);
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!");
- AMemoryHelper.FillWithZeros(Memory, Src, (int)Size);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission);
- SharedMem.AddVirtualPosition(Memory, Src, Size);
+ return;
+ }
- Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
+ KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle);
+
+ if (SharedMemory == null)
+ {
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!");
- lock (MappedSharedMems)
- {
- MappedSharedMems.Add((SharedMem, Src, Size));
- }
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+ return;
+ }
- ThreadState.X0 = 0;
+ 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}!");
}
- //TODO: Error codes.
+ 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<HSharedMem>(Handle);
+ 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 (SharedMem != null)
+ if ((ulong)(Position + Size) <= (ulong)Position)
{
- Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory);
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
- SharedMem.RemoveVirtualPosition(Memory, Src, Size);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
- lock (MappedSharedMems)
- {
- MappedSharedMems.Remove((SharedMem, Src, Size));
- }
+ return;
+ }
- ThreadState.X0 = 0;
+ KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle);
+
+ if (SharedMemory == null)
+ {
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!");
+
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+ return;
}
- //TODO: Error codes.
+ 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;
+ }
+
+ 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.InvalidAddress);
return;
}
- AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src);
+ 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;
+ }
- Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
+ Process.MemoryManager.ReserveTransferMemory(Position, Size, Permission);
- HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size);
+ KTransferMemory TransferMemory = new KTransferMemory(Position, Size);
- ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem);
+ 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;
+
+ if (!PageAligned(Position))
+ {
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
- Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
- ThreadState.X0 = 0;
+ 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;
+
+ if (!PageAligned(Position))
+ {
+ Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
- Memory.Manager.Unmap(Position, Size);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
- ThreadState.X0 = 0;
+ 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 PageAligned(long Position)
+ {
+ return (Position & (KMemoryManager.PageSize - 1)) == 0;
+ }
+
+ private bool InsideAddrSpace(long Position, long Size)
+ {
+ ulong Start = (ulong)Position;
+ ulong End = (ulong)Size + Start;
+
+ return Start >= (ulong)Process.MemoryManager.AddrSpaceStart &&
+ End < (ulong)Process.MemoryManager.AddrSpaceEnd;
}
- private static bool IsValidPosition(long Position)
+ private bool InsideMapRegion(long Position, long Size)
{
- return Position >= MemoryRegions.AddrSpaceStart &&
- Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
+ ulong Start = (ulong)Position;
+ ulong End = (ulong)Size + Start;
+
+ return Start >= (ulong)Process.MemoryManager.MapRegionStart &&
+ End < (ulong)Process.MemoryManager.MapRegionEnd;
}
- private static bool IsValidMapPosition(long Position)
+ private bool InsideHeapRegion(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.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 08305522..f833745b 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 8702203e..04524850 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 9fc42617..164a85ca 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 8696aa4d..00000000
--- 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 198c621c..00000000
--- 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 6cbc0860..96fe3610 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<KTlsPageManager> TlsPages;
+
public KProcessScheduler Scheduler { get; private set; }
public List<KThread> 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<int, AThread> TlsSlots;
-
private ConcurrentDictionary<long, KThread> 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<KTlsPageManager>();
ThreadArbiterList = new List<KThread>();
@@ -88,18 +92,11 @@ namespace Ryujinx.HLE.OsHle
SvcHandler = new SvcHandler(Ns, this);
- TlsSlots = new ConcurrentDictionary<int, AThread>();
-
Threads = new ConcurrentDictionary<long, KThread>();
Executables = new List<Executable>();
- 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 MainStackSize = 1 * 1024 * 1024;
- long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize;
+ long MainStackBottom = MainStackTop - MainStackSize;
- int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 44, 0);
+ 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 2ef67cc3..5c32ca83 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<int, ServiceProcessRequest> Commands => m_Commands;
- private HSharedMem HidSharedMem;
+ private KSharedMemory HidSharedMem;
- public IAppletResource(HSharedMem HidSharedMem)
+ public IAppletResource(KSharedMemory HidSharedMem)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
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 00000000..e718182a
--- /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<long, Range> Maps;
+ private SortedList<long, Range> Reservations;
+
+ public NvGpuASCtx(ServiceCtx Context)
+ {
+ Vmm = new NvGpuVmm(Context.Memory);
+
+ Maps = new SortedList<long, Range>();
+ Reservations = new SortedList<long, Range>();
+ }
+
+ 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<long, Range> 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<long, Range> 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 fcc478a4..3c67cef0 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<Process, NvGpuVmm> Vmms;
+ private const int FlagRemapSubRange = 0x100;
+
+ private static ConcurrentDictionary<Process, NvGpuASCtx> ASCtxs;
static NvGpuASIoctl()
{
- Vmms = new ConcurrentDictionary<Process, NvGpuVmm>();
+ ASCtxs = new ConcurrentDictionary<Process, NvGpuASCtx>();
}
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<NvGpuASAllocSpace>(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<NvGpuASAllocSpace>(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<NvGpuASUnmapBuffer>(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<NvGpuASMapBufferEx>(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!");
-
- return NvResult.InvalidInput;
+ if (ASCtx.ValidateFixedBuffer(Args.Offset, Size))
+ {
+ Args.Offset = ASCtx.Vmm.Map(PA, Args.Offset, Size);
+ }
+ else
+ {
+ string Msg = string.Format(MapErrorMsg, Args.Offset, Size);
+
+ 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<NvGpuASRemap>(Context.Memory, InputPosition);
+ for (int Index = 0; Index < Count; Index++, InputPosition += 0x14)
+ {
+ NvGpuASRemap Args = AMemoryHelper.Read<NvGpuASRemap>(Context.Memory, InputPosition);
- NvGpuVmm Vmm = GetVmm(Context);
+ NvGpuVmm Vmm = GetASCtx(Context).Vmm;
- NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle);
+ NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle);
- if (Map == null)
- {
- Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!");
+ if (Map == null)
+ {
+ Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!");
- return NvResult.InvalidInput;
- }
+ return NvResult.InvalidInput;
+ }
- //FIXME: This is most likely wrong...
- Vmm.Map(Map.Address, (long)(uint)Args.Offset << 16,
- (long)(uint)Args.Pages << 16);
+ 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;
+ }
+ }
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 411a579a..3e030643 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<NvHostChannelSubmitGpfifo>(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<NvHostChannelSubmitGpfifo>(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 7705a1f7..7f203453 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 1e13f197..bc66fc71 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 21fce700..f349a03e 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 4c6107f8..e5b29825 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 b8447ac6..4788c5af 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;
-
- long FontSizeBufferPosition = Context.Request.ReceiveBuff[2].Position;
- long FontSizeBufferSize = Context.Request.ReceiveBuff[2].Size;
+ for (SharedFontType Type = 0; Type < SharedFontType.Count; Type++)
+ {
+ int Offset = (int)Type * 4;
- LoadState = Context.Ns.Font.GetLoadState(FontType);
+ if (!AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, Offset))
+ {
+ break;
+ }
- if (BufferPos >= TypesSize || BufferPos >= OffsetsSize || BufferPos >= FontSizeBufferSize)
- {
- return 0;
+ LoadedCount++;
}
- 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));
-
- BufferPos += 4;
+ Context.ResponseData.Write(LoadedCount);
+ Context.ResponseData.Write((int)SharedFontType.Count);
- return BufferPos;
+ return 0;
}
- public long GetSharedFontInOrderOfPriority(ServiceCtx Context)
+ private bool AddFontToOrderOfPriorityList(ServiceCtx Context, SharedFontType FontType, int Offset)
{
- ulong LanguageCode = Context.RequestData.ReadUInt64();
- uint LoadedCount = 0;
- uint BufferPos = 0;
- uint Loaded = 0;
+ 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;
- for (int Type = 0; Type < Context.Ns.Font.Count; Type++)
+ if ((uint)Offset + 4 > (uint)TypesSize ||
+ (uint)Offset + 4 > (uint)OffsetsSize ||
+ (uint)Offset + 4 > (uint)FontSizeBufferSize)
{
- BufferPos = AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, BufferPos, out Loaded);
- LoadedCount += Loaded;
+ return false;
}
- Context.ResponseData.Write(LoadedCount);
- Context.ResponseData.Write(Context.Ns.Font.Count);
+ Context.Memory.WriteInt32(TypesPosition + Offset, (int)FontType);
- return 0;
+ Context.Memory.WriteInt32(OffsetsPosition + Offset, Context.Ns.Os.Font.GetSharedMemoryAddressOffset(FontType));
+
+ Context.Memory.WriteInt32(FontSizeBufferPosition + Offset, Context.Ns.Os.Font.GetFontSize(FontType));
+
+ 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 41f2916f..04b50bb3 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 39cced28..010dbb20 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 9bc3d6b4..2dabd9a0 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 475e5116..165922e9 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 70a8e192..e6a02379 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 14542706..ec0f05b7 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")));