|
@@ -1,6 +1,7 @@
|
|
|
using ChocolArm64.Exceptions;
|
|
using ChocolArm64.Exceptions;
|
|
|
using ChocolArm64.State;
|
|
using ChocolArm64.State;
|
|
|
using System;
|
|
using System;
|
|
|
|
|
+using System.Collections.Concurrent;
|
|
|
using System.Collections.Generic;
|
|
using System.Collections.Generic;
|
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.CompilerServices;
|
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.InteropServices;
|
|
@@ -12,9 +13,22 @@ namespace ChocolArm64.Memory
|
|
|
{
|
|
{
|
|
|
public unsafe class AMemory : IAMemory, IDisposable
|
|
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
|
|
private class ArmMonitor
|
|
|
{
|
|
{
|
|
@@ -29,32 +43,30 @@ namespace ChocolArm64.Memory
|
|
|
|
|
|
|
|
private Dictionary<int, ArmMonitor> Monitors;
|
|
private Dictionary<int, ArmMonitor> Monitors;
|
|
|
|
|
|
|
|
|
|
+ private ConcurrentDictionary<long, IntPtr> ObservedPages;
|
|
|
|
|
+
|
|
|
public IntPtr Ram { get; private set; }
|
|
public IntPtr Ram { get; private set; }
|
|
|
|
|
|
|
|
private byte* RamPtr;
|
|
private byte* RamPtr;
|
|
|
|
|
|
|
|
- private int HostPageSize;
|
|
|
|
|
|
|
+ private byte*** PageTable;
|
|
|
|
|
|
|
|
- public AMemory()
|
|
|
|
|
|
|
+ public AMemory(IntPtr Ram)
|
|
|
{
|
|
{
|
|
|
- Manager = new AMemoryMgr();
|
|
|
|
|
-
|
|
|
|
|
Monitors = new Dictionary<int, ArmMonitor>();
|
|
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)
|
|
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)
|
|
public sbyte ReadSByte(long Position)
|
|
|
{
|
|
{
|
|
|
return (sbyte)ReadByte(Position);
|
|
return (sbyte)ReadByte(Position);
|
|
@@ -233,33 +189,22 @@ namespace ChocolArm64.Memory
|
|
|
|
|
|
|
|
public byte ReadByte(long Position)
|
|
public byte ReadByte(long Position)
|
|
|
{
|
|
{
|
|
|
- EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
|
|
|
|
-
|
|
|
|
|
- return ReadByteUnchecked(Position);
|
|
|
|
|
|
|
+ return *((byte*)Translate(Position));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public ushort ReadUInt16(long 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)
|
|
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)
|
|
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)
|
|
public Vector128<float> ReadVector8(long Position)
|
|
@@ -274,6 +219,7 @@ namespace ChocolArm64.Memory
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
public Vector128<float> ReadVector16(long Position)
|
|
public Vector128<float> ReadVector16(long Position)
|
|
|
{
|
|
{
|
|
|
if (Sse2.IsSupported)
|
|
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)]
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
|
- public Vector128<float> ReadVector32Unchecked(long Position)
|
|
|
|
|
|
|
+ public Vector128<float> ReadVector32(long Position)
|
|
|
{
|
|
{
|
|
|
if (Sse.IsSupported)
|
|
if (Sse.IsSupported)
|
|
|
{
|
|
{
|
|
|
- return Sse.LoadScalarVector128((float*)(RamPtr + (uint)Position));
|
|
|
|
|
|
|
+ return Sse.LoadScalarVector128((float*)Translate(Position));
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
@@ -410,11 +246,11 @@ namespace ChocolArm64.Memory
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
|
- public Vector128<float> ReadVector64Unchecked(long Position)
|
|
|
|
|
|
|
+ public Vector128<float> ReadVector64(long Position)
|
|
|
{
|
|
{
|
|
|
if (Sse2.IsSupported)
|
|
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
|
|
else
|
|
|
{
|
|
{
|
|
@@ -423,11 +259,11 @@ namespace ChocolArm64.Memory
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
- public Vector128<float> ReadVector128Unchecked(long Position)
|
|
|
|
|
|
|
+ public Vector128<float> ReadVector128(long Position)
|
|
|
{
|
|
{
|
|
|
if (Sse.IsSupported)
|
|
if (Sse.IsSupported)
|
|
|
{
|
|
{
|
|
|
- return Sse.LoadVector128((float*)(RamPtr + (uint)Position));
|
|
|
|
|
|
|
+ return Sse.LoadVector128((float*)Translate(Position));
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
@@ -442,11 +278,11 @@ namespace ChocolArm64.Memory
|
|
|
throw new ArgumentOutOfRangeException(nameof(Size));
|
|
throw new ArgumentOutOfRangeException(nameof(Size));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- EnsureRangeIsValid(Position, Size, AMemoryPerm.Read);
|
|
|
|
|
|
|
+ EnsureRangeIsValid(Position, Size);
|
|
|
|
|
|
|
|
byte[] Data = new byte[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;
|
|
return Data;
|
|
|
}
|
|
}
|
|
@@ -473,35 +309,25 @@ namespace ChocolArm64.Memory
|
|
|
|
|
|
|
|
public void WriteByte(long Position, byte Value)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
public void WriteVector8(long Position, Vector128<float> Value)
|
|
|
{
|
|
{
|
|
|
if (Sse41.IsSupported)
|
|
if (Sse41.IsSupported)
|
|
@@ -518,6 +344,7 @@ namespace ChocolArm64.Memory
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
public void WriteVector16(long Position, Vector128<float> Value)
|
|
public void WriteVector16(long Position, Vector128<float> Value)
|
|
|
{
|
|
{
|
|
|
if (Sse2.IsSupported)
|
|
if (Sse2.IsSupported)
|
|
@@ -530,14 +357,12 @@ namespace ChocolArm64.Memory
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ [MethodImpl(MethodImplOptions.NoInlining)]
|
|
|
public void WriteVector32(long Position, Vector128<float> Value)
|
|
public void WriteVector32(long Position, Vector128<float> Value)
|
|
|
{
|
|
{
|
|
|
- EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
|
|
|
|
- EnsureAccessIsValid(Position + 3, AMemoryPerm.Write);
|
|
|
|
|
-
|
|
|
|
|
if (Sse.IsSupported)
|
|
if (Sse.IsSupported)
|
|
|
{
|
|
{
|
|
|
- Sse.StoreScalar((float*)(RamPtr + (uint)Position), Value);
|
|
|
|
|
|
|
+ Sse.StoreScalar((float*)TranslateWrite(Position), Value);
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
@@ -545,14 +370,12 @@ namespace ChocolArm64.Memory
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ [MethodImpl(MethodImplOptions.NoInlining)]
|
|
|
public void WriteVector64(long Position, Vector128<float> Value)
|
|
public void WriteVector64(long Position, Vector128<float> Value)
|
|
|
{
|
|
{
|
|
|
- EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
|
|
|
|
- EnsureAccessIsValid(Position + 7, AMemoryPerm.Write);
|
|
|
|
|
-
|
|
|
|
|
if (Sse2.IsSupported)
|
|
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
|
|
else
|
|
|
{
|
|
{
|
|
@@ -560,14 +383,12 @@ namespace ChocolArm64.Memory
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
public void WriteVector128(long Position, Vector128<float> Value)
|
|
public void WriteVector128(long Position, Vector128<float> Value)
|
|
|
{
|
|
{
|
|
|
- EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
|
|
|
|
- EnsureAccessIsValid(Position + 15, AMemoryPerm.Write);
|
|
|
|
|
-
|
|
|
|
|
if (Sse.IsSupported)
|
|
if (Sse.IsSupported)
|
|
|
{
|
|
{
|
|
|
- Sse.Store((float*)(RamPtr + (uint)Position), Value);
|
|
|
|
|
|
|
+ Sse.Store((float*)TranslateWrite(Position), Value);
|
|
|
}
|
|
}
|
|
|
else
|
|
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;
|
|
long EndPos = Position + Size;
|
|
|
|
|
|
|
|
- Position &= ~AMemoryMgr.PageMask;
|
|
|
|
|
|
|
+ Position &= ~PageMask;
|
|
|
|
|
+
|
|
|
|
|
+ long ExpectedPA = GetPhysicalAddress(Position);
|
|
|
|
|
|
|
|
while ((ulong)Position < (ulong)EndPos)
|
|
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()
|
|
public void Dispose()
|
|
@@ -725,19 +686,24 @@ namespace ChocolArm64.Memory
|
|
|
|
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
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;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|