aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Memory
diff options
context:
space:
mode:
authorAlex Barney <thealexbarney@gmail.com>2018-10-30 19:43:02 -0600
committergdkchan <gab.dark.100@gmail.com>2018-10-30 22:43:02 -0300
commit9cb57fb4bb3bbae0ae052a5af4a96a49fc5d864d (patch)
tree0c97425aeb311c142bc92a6fcc503cb2c07d4376 /ChocolArm64/Memory
parent5a87e58183578f5b84ca8d01cbb76aed11820f78 (diff)
Adjust naming conventions for Ryujinx and ChocolArm64 projects (#484)
* Change naming convention for Ryujinx project * Change naming convention for ChocolArm64 project * Fix NaN * Remove unneeded this. from Ryujinx project * Adjust naming from new PRs * Name changes based on feedback * How did this get removed? * Rebasing fix * Change FP enum case * Remove prefix from ChocolArm64 classes - Part 1 * Remove prefix from ChocolArm64 classes - Part 2 * Fix alignment from last commit's renaming * Rename namespaces * Rename stragglers * Fix alignment * Rename OpCode class * Missed a few * Adjust alignment
Diffstat (limited to 'ChocolArm64/Memory')
-rw-r--r--ChocolArm64/Memory/AMemory.cs745
-rw-r--r--ChocolArm64/Memory/AMemoryHelper.cs67
-rw-r--r--ChocolArm64/Memory/IAMemory.cs37
-rw-r--r--ChocolArm64/Memory/IMemory.cs37
-rw-r--r--ChocolArm64/Memory/MemoryHelper.cs67
-rw-r--r--ChocolArm64/Memory/MemoryManager.cs745
6 files changed, 849 insertions, 849 deletions
diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs
deleted file mode 100644
index 2854871e..00000000
--- a/ChocolArm64/Memory/AMemory.cs
+++ /dev/null
@@ -1,745 +0,0 @@
-using ChocolArm64.Events;
-using ChocolArm64.Exceptions;
-using ChocolArm64.State;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Runtime.Intrinsics;
-using System.Runtime.Intrinsics.X86;
-using System.Threading;
-
-namespace ChocolArm64.Memory
-{
- public unsafe class AMemory : IAMemory, IDisposable
- {
- 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;
-
- private const int PTLvl0Bit = PTPageBits + PTLvl1Bits;
- private const int PTLvl1Bit = PTPageBits;
-
- private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;
-
- private class ArmMonitor
- {
- public long Position;
- public bool ExState;
-
- public bool HasExclusiveAccess(long Position)
- {
- return this.Position == Position && ExState;
- }
- }
-
- private Dictionary<int, ArmMonitor> Monitors;
-
- private ConcurrentDictionary<long, IntPtr> ObservedPages;
-
- public IntPtr Ram { get; private set; }
-
- private byte* RamPtr;
-
- private byte*** PageTable;
-
- public event EventHandler<AInvalidAccessEventArgs> InvalidAccess;
-
- public AMemory(IntPtr Ram)
- {
- Monitors = new Dictionary<int, ArmMonitor>();
-
- ObservedPages = new ConcurrentDictionary<long, IntPtr>();
-
- this.Ram = Ram;
-
- RamPtr = (byte*)Ram;
-
- PageTable = (byte***)Marshal.AllocHGlobal(PTLvl0Size * IntPtr.Size);
-
- for (int L0 = 0; L0 < PTLvl0Size; L0++)
- {
- PageTable[L0] = null;
- }
- }
-
- public void RemoveMonitor(int Core)
- {
- lock (Monitors)
- {
- ClearExclusive(Core);
-
- Monitors.Remove(Core);
- }
- }
-
- public void SetExclusive(int Core, long Position)
- {
- Position &= ~ErgMask;
-
- lock (Monitors)
- {
- foreach (ArmMonitor Mon in Monitors.Values)
- {
- if (Mon.Position == Position && Mon.ExState)
- {
- Mon.ExState = false;
- }
- }
-
- if (!Monitors.TryGetValue(Core, out ArmMonitor ThreadMon))
- {
- ThreadMon = new ArmMonitor();
-
- Monitors.Add(Core, ThreadMon);
- }
-
- ThreadMon.Position = Position;
- ThreadMon.ExState = true;
- }
- }
-
- public bool TestExclusive(int Core, long Position)
- {
- //Note: Any call to this method also should be followed by a
- //call to ClearExclusiveForStore if this method returns true.
- Position &= ~ErgMask;
-
- Monitor.Enter(Monitors);
-
- if (!Monitors.TryGetValue(Core, out ArmMonitor ThreadMon))
- {
- return false;
- }
-
- bool ExState = ThreadMon.HasExclusiveAccess(Position);
-
- if (!ExState)
- {
- Monitor.Exit(Monitors);
- }
-
- return ExState;
- }
-
- public void ClearExclusiveForStore(int Core)
- {
- if (Monitors.TryGetValue(Core, out ArmMonitor ThreadMon))
- {
- ThreadMon.ExState = false;
- }
-
- Monitor.Exit(Monitors);
- }
-
- public void ClearExclusive(int Core)
- {
- lock (Monitors)
- {
- if (Monitors.TryGetValue(Core, out ArmMonitor ThreadMon))
- {
- ThreadMon.ExState = false;
- }
- }
- }
-
- public void WriteInt32ToSharedAddr(long Position, int Value)
- {
- long MaskedPosition = Position & ~ErgMask;
-
- lock (Monitors)
- {
- foreach (ArmMonitor Mon in Monitors.Values)
- {
- if (Mon.Position == MaskedPosition && Mon.ExState)
- {
- Mon.ExState = false;
- }
- }
-
- WriteInt32(Position, Value);
- }
- }
-
- 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 byte ReadByte(long Position)
- {
- return *((byte*)Translate(Position));
- }
-
- public ushort ReadUInt16(long Position)
- {
- return *((ushort*)Translate(Position));
- }
-
- public uint ReadUInt32(long Position)
- {
- return *((uint*)Translate(Position));
- }
-
- public ulong ReadUInt64(long Position)
- {
- return *((ulong*)Translate(Position));
- }
-
- public Vector128<float> ReadVector8(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> ReadVector16(long Position)
- {
- if (Sse2.IsSupported)
- {
- return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse2.SetZeroVector128<ushort>(), ReadUInt16(Position), 0));
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Vector128<float> ReadVector32(long Position)
- {
- if (Sse.IsSupported)
- {
- return Sse.LoadScalarVector128((float*)Translate(Position));
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Vector128<float> ReadVector64(long Position)
- {
- if (Sse2.IsSupported)
- {
- return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)Translate(Position)));
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Vector128<float> ReadVector128(long Position)
- {
- if (Sse.IsSupported)
- {
- return Sse.LoadVector128((float*)Translate(Position));
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- public byte[] ReadBytes(long Position, long Size)
- {
- if ((uint)Size > int.MaxValue)
- {
- throw new ArgumentOutOfRangeException(nameof(Size));
- }
-
- EnsureRangeIsValid(Position, Size);
-
- byte[] Data = new byte[Size];
-
- Marshal.Copy((IntPtr)Translate(Position), Data, 0, (int)Size);
-
- return Data;
- }
-
- public void ReadBytes(long Position, byte[] Data, int StartIndex, int Size)
- {
- //Note: This will be moved later.
- EnsureRangeIsValid(Position, (uint)Size);
-
- Marshal.Copy((IntPtr)Translate(Position), Data, StartIndex, Size);
- }
-
- 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 void WriteByte(long Position, byte Value)
- {
- *((byte*)TranslateWrite(Position)) = Value;
- }
-
- public void WriteUInt16(long Position, ushort Value)
- {
- *((ushort*)TranslateWrite(Position)) = Value;
- }
-
- public void WriteUInt32(long Position, uint Value)
- {
- *((uint*)TranslateWrite(Position)) = Value;
- }
-
- public void WriteUInt64(long Position, ulong Value)
- {
- *((ulong*)TranslateWrite(Position)) = Value;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteVector8(long Position, Vector128<float> Value)
- {
- if (Sse41.IsSupported)
- {
- WriteByte(Position, Sse41.Extract(Sse.StaticCast<float, byte>(Value), 0));
- }
- else if (Sse2.IsSupported)
- {
- WriteByte(Position, (byte)Sse2.Extract(Sse.StaticCast<float, ushort>(Value), 0));
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteVector16(long Position, Vector128<float> Value)
- {
- if (Sse2.IsSupported)
- {
- WriteUInt16(Position, Sse2.Extract(Sse.StaticCast<float, ushort>(Value), 0));
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteVector32(long Position, Vector128<float> Value)
- {
- if (Sse.IsSupported)
- {
- Sse.StoreScalar((float*)TranslateWrite(Position), Value);
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteVector64(long Position, Vector128<float> Value)
- {
- if (Sse2.IsSupported)
- {
- Sse2.StoreScalar((double*)TranslateWrite(Position), Sse.StaticCast<float, double>(Value));
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteVector128(long Position, Vector128<float> Value)
- {
- if (Sse.IsSupported)
- {
- Sse.Store((float*)TranslateWrite(Position), Value);
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- public void WriteBytes(long Position, byte[] Data)
- {
- EnsureRangeIsValid(Position, (uint)Data.Length);
-
- Marshal.Copy(Data, 0, (IntPtr)TranslateWrite(Position), Data.Length);
- }
-
- public void WriteBytes(long Position, byte[] Data, int StartIndex, int Size)
- {
- //Note: This will be moved later.
- //Using Translate instead of TranslateWrite is on purpose.
- EnsureRangeIsValid(Position, (uint)Size);
-
- Marshal.Copy(Data, StartIndex, (IntPtr)Translate(Position), Size);
- }
-
- public void CopyBytes(long Src, long Dst, long Size)
- {
- //Note: This will be moved later.
- EnsureRangeIsValid(Src, Size);
- EnsureRangeIsValid(Dst, Size);
-
- byte* SrcPtr = Translate(Src);
- byte* DstPtr = TranslateWrite(Dst);
-
- Buffer.MemoryCopy(SrcPtr, DstPtr, Size, Size);
- }
-
- public void Map(long VA, long PA, long Size)
- {
- SetPTEntries(VA, RamPtr + PA, Size);
- }
-
- public void Unmap(long Position, long Size)
- {
- SetPTEntries(Position, null, Size);
-
- StopObservingRegion(Position, Size);
- }
-
- public bool IsMapped(long Position)
- {
- if (!(IsValidPosition(Position)))
- {
- return false;
- }
-
- long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
- long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
-
- if (PageTable[L0] == null)
- {
- return false;
- }
-
- return PageTable[L0][L1] != null || ObservedPages.ContainsKey(Position >> PTPageBits);
- }
-
- public long GetPhysicalAddress(long VirtualAddress)
- {
- byte* Ptr = Translate(VirtualAddress);
-
- return (long)(Ptr - RamPtr);
- }
-
- internal byte* Translate(long Position)
- {
- long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
- long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
-
- long Old = Position;
-
- byte** Lvl1 = PageTable[L0];
-
- if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0)
- {
- goto Unmapped;
- }
-
- if (Lvl1 == null)
- {
- goto Unmapped;
- }
-
- Position &= PageMask;
-
- byte* Ptr = Lvl1[L1];
-
- if (Ptr == null)
- {
- goto Unmapped;
- }
-
- return Ptr + Position;
-
-Unmapped:
- return HandleNullPte(Old);
- }
-
- private byte* HandleNullPte(long Position)
- {
- long Key = Position >> PTPageBits;
-
- if (ObservedPages.TryGetValue(Key, out IntPtr Ptr))
- {
- return (byte*)Ptr + (Position & PageMask);
- }
-
- InvalidAccess?.Invoke(this, new AInvalidAccessEventArgs(Position));
-
- throw new VmmPageFaultException(Position);
- }
-
- internal byte* TranslateWrite(long Position)
- {
- long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
- long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
-
- long Old = Position;
-
- byte** Lvl1 = PageTable[L0];
-
- if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0)
- {
- goto Unmapped;
- }
-
- if (Lvl1 == null)
- {
- goto Unmapped;
- }
-
- Position &= PageMask;
-
- byte* Ptr = Lvl1[L1];
-
- if (Ptr == null)
- {
- goto Unmapped;
- }
-
- return Ptr + Position;
-
-Unmapped:
- return HandleNullPteWrite(Old);
- }
-
- private byte* HandleNullPteWrite(long Position)
- {
- long Key = Position >> PTPageBits;
-
- if (ObservedPages.TryGetValue(Key, out IntPtr Ptr))
- {
- SetPTEntry(Position, (byte*)Ptr);
-
- return (byte*)Ptr + (Position & PageMask);
- }
-
- InvalidAccess?.Invoke(this, new AInvalidAccessEventArgs(Position));
-
- throw new VmmPageFaultException(Position);
- }
-
- private void SetPTEntries(long VA, byte* Ptr, long Size)
- {
- long EndPosition = (VA + Size + PageMask) & ~PageMask;
-
- while ((ulong)VA < (ulong)EndPosition)
- {
- SetPTEntry(VA, Ptr);
-
- VA += PageSize;
-
- if (Ptr != null)
- {
- Ptr += PageSize;
- }
- }
- }
-
- private void SetPTEntry(long Position, byte* Ptr)
- {
- if (!IsValidPosition(Position))
- {
- throw new ArgumentOutOfRangeException(nameof(Position));
- }
-
- long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
- long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
-
- if (PageTable[L0] == null)
- {
- byte** Lvl1 = (byte**)Marshal.AllocHGlobal(PTLvl1Size * IntPtr.Size);
-
- for (int ZL1 = 0; ZL1 < PTLvl1Size; ZL1++)
- {
- Lvl1[ZL1] = null;
- }
-
- Thread.MemoryBarrier();
-
- PageTable[L0] = Lvl1;
- }
-
- PageTable[L0][L1] = Ptr;
- }
-
- public (bool[], int) IsRegionModified(long Position, long Size)
- {
- long EndPosition = (Position + Size + PageMask) & ~PageMask;
-
- Position &= ~PageMask;
-
- Size = EndPosition - Position;
-
- bool[] Modified = new bool[Size >> PTPageBits];
-
- int Count = 0;
-
- lock (ObservedPages)
- {
- for (int Page = 0; Page < Modified.Length; Page++)
- {
- byte* Ptr = Translate(Position);
-
- if (ObservedPages.TryAdd(Position >> PTPageBits, (IntPtr)Ptr))
- {
- Modified[Page] = true;
-
- Count++;
- }
- else
- {
- long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
- long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
-
- byte** Lvl1 = PageTable[L0];
-
- if (Lvl1 != null)
- {
- if (Modified[Page] = Lvl1[L1] != null)
- {
- Count++;
- }
- }
- }
-
- SetPTEntry(Position, null);
-
- Position += PageSize;
- }
- }
-
- return (Modified, Count);
- }
-
- public void StopObservingRegion(long Position, long Size)
- {
- long EndPosition = (Position + Size + PageMask) & ~PageMask;
-
- while (Position < EndPosition)
- {
- lock (ObservedPages)
- {
- if (ObservedPages.TryRemove(Position >> PTPageBits, out IntPtr Ptr))
- {
- SetPTEntry(Position, (byte*)Ptr);
- }
- }
-
- Position += PageSize;
- }
- }
-
- public IntPtr GetHostAddress(long Position, long Size)
- {
- EnsureRangeIsValid(Position, Size);
-
- return (IntPtr)Translate(Position);
- }
-
- internal void EnsureRangeIsValid(long Position, long Size)
- {
- long EndPos = Position + Size;
-
- Position &= ~PageMask;
-
- long ExpectedPA = GetPhysicalAddress(Position);
-
- while ((ulong)Position < (ulong)EndPos)
- {
- long PA = GetPhysicalAddress(Position);
-
- if (PA != ExpectedPA)
- {
- throw new VmmAccessException(Position, Size);
- }
-
- Position += PageSize;
- ExpectedPA += PageSize;
- }
- }
-
- public bool IsValidPosition(long Position)
- {
- return Position >> (PTLvl0Bits + PTLvl1Bits + PTPageBits) == 0;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (PageTable == null)
- {
- return;
- }
-
- for (int L0 = 0; L0 < PTLvl0Size; L0++)
- {
- if (PageTable[L0] != null)
- {
- Marshal.FreeHGlobal((IntPtr)PageTable[L0]);
- }
-
- 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
deleted file mode 100644
index ea877834..00000000
--- a/ChocolArm64/Memory/AMemoryHelper.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System;
-using System.IO;
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace ChocolArm64.Memory
-{
- public static class AMemoryHelper
- {
- public static void FillWithZeros(AMemory Memory, long Position, int Size)
- {
- int Size8 = Size & ~(8 - 1);
-
- for (int Offs = 0; Offs < Size8; Offs += 8)
- {
- Memory.WriteInt64(Position + Offs, 0);
- }
-
- for (int Offs = Size8; Offs < (Size - Size8); Offs++)
- {
- Memory.WriteByte(Position + Offs, 0);
- }
- }
-
- public unsafe static T Read<T>(AMemory Memory, long Position) where T : struct
- {
- long Size = Marshal.SizeOf<T>();
-
- Memory.EnsureRangeIsValid(Position, Size);
-
- IntPtr Ptr = (IntPtr)Memory.Translate(Position);
-
- return Marshal.PtrToStructure<T>(Ptr);
- }
-
- public unsafe static void Write<T>(AMemory Memory, long Position, T Value) where T : struct
- {
- long Size = Marshal.SizeOf<T>();
-
- Memory.EnsureRangeIsValid(Position, Size);
-
- IntPtr Ptr = (IntPtr)Memory.TranslateWrite(Position);
-
- Marshal.StructureToPtr<T>(Value, Ptr, false);
- }
-
- public static string ReadAsciiString(AMemory Memory, long Position, long MaxSize = -1)
- {
- using (MemoryStream MS = new MemoryStream())
- {
- for (long Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++)
- {
- byte Value = (byte)Memory.ReadByte(Position + Offs);
-
- if (Value == 0)
- {
- break;
- }
-
- MS.WriteByte(Value);
- }
-
- return Encoding.ASCII.GetString(MS.ToArray());
- }
- }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Memory/IAMemory.cs b/ChocolArm64/Memory/IAMemory.cs
deleted file mode 100644
index 5b7d17bb..00000000
--- a/ChocolArm64/Memory/IAMemory.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-namespace ChocolArm64.Memory
-{
- public interface IAMemory
- {
- sbyte ReadSByte(long Position);
-
- short ReadInt16(long Position);
-
- int ReadInt32(long Position);
-
- long ReadInt64(long Position);
-
- byte ReadByte(long Position);
-
- ushort ReadUInt16(long Position);
-
- uint ReadUInt32(long Position);
-
- ulong ReadUInt64(long Position);
-
- void WriteSByte(long Position, sbyte Value);
-
- void WriteInt16(long Position, short Value);
-
- void WriteInt32(long Position, int Value);
-
- void WriteInt64(long Position, long Value);
-
- void WriteByte(long Position, byte Value);
-
- void WriteUInt16(long Position, ushort Value);
-
- void WriteUInt32(long Position, uint Value);
-
- void WriteUInt64(long Position, ulong Value);
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Memory/IMemory.cs b/ChocolArm64/Memory/IMemory.cs
new file mode 100644
index 00000000..dc582155
--- /dev/null
+++ b/ChocolArm64/Memory/IMemory.cs
@@ -0,0 +1,37 @@
+namespace ChocolArm64.Memory
+{
+ public interface IMemory
+ {
+ sbyte ReadSByte(long position);
+
+ short ReadInt16(long position);
+
+ int ReadInt32(long position);
+
+ long ReadInt64(long position);
+
+ byte ReadByte(long position);
+
+ ushort ReadUInt16(long position);
+
+ uint ReadUInt32(long position);
+
+ ulong ReadUInt64(long position);
+
+ void WriteSByte(long position, sbyte value);
+
+ void WriteInt16(long position, short value);
+
+ void WriteInt32(long position, int value);
+
+ void WriteInt64(long position, long value);
+
+ void WriteByte(long position, byte value);
+
+ void WriteUInt16(long position, ushort value);
+
+ void WriteUInt32(long position, uint value);
+
+ void WriteUInt64(long position, ulong value);
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Memory/MemoryHelper.cs b/ChocolArm64/Memory/MemoryHelper.cs
new file mode 100644
index 00000000..2e721afa
--- /dev/null
+++ b/ChocolArm64/Memory/MemoryHelper.cs
@@ -0,0 +1,67 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace ChocolArm64.Memory
+{
+ public static class MemoryHelper
+ {
+ public static void FillWithZeros(MemoryManager memory, long position, int size)
+ {
+ int size8 = size & ~(8 - 1);
+
+ for (int offs = 0; offs < size8; offs += 8)
+ {
+ memory.WriteInt64(position + offs, 0);
+ }
+
+ for (int offs = size8; offs < (size - size8); offs++)
+ {
+ memory.WriteByte(position + offs, 0);
+ }
+ }
+
+ public unsafe static T Read<T>(MemoryManager memory, long position) where T : struct
+ {
+ long size = Marshal.SizeOf<T>();
+
+ memory.EnsureRangeIsValid(position, size);
+
+ IntPtr ptr = (IntPtr)memory.Translate(position);
+
+ return Marshal.PtrToStructure<T>(ptr);
+ }
+
+ public unsafe static void Write<T>(MemoryManager memory, long position, T value) where T : struct
+ {
+ long size = Marshal.SizeOf<T>();
+
+ memory.EnsureRangeIsValid(position, size);
+
+ IntPtr ptr = (IntPtr)memory.TranslateWrite(position);
+
+ Marshal.StructureToPtr<T>(value, ptr, false);
+ }
+
+ public static string ReadAsciiString(MemoryManager memory, long position, long maxSize = -1)
+ {
+ using (MemoryStream ms = new MemoryStream())
+ {
+ for (long offs = 0; offs < maxSize || maxSize == -1; offs++)
+ {
+ byte value = (byte)memory.ReadByte(position + offs);
+
+ if (value == 0)
+ {
+ break;
+ }
+
+ ms.WriteByte(value);
+ }
+
+ return Encoding.ASCII.GetString(ms.ToArray());
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Memory/MemoryManager.cs b/ChocolArm64/Memory/MemoryManager.cs
new file mode 100644
index 00000000..308dd17e
--- /dev/null
+++ b/ChocolArm64/Memory/MemoryManager.cs
@@ -0,0 +1,745 @@
+using ChocolArm64.Events;
+using ChocolArm64.Exceptions;
+using ChocolArm64.State;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+using System.Threading;
+
+namespace ChocolArm64.Memory
+{
+ public unsafe class MemoryManager : IMemory, IDisposable
+ {
+ 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;
+
+ private const int PtLvl0Bit = PtPageBits + PtLvl1Bits;
+ private const int PtLvl1Bit = PtPageBits;
+
+ private const long ErgMask = (4 << CpuThreadState.ErgSizeLog2) - 1;
+
+ private class ArmMonitor
+ {
+ public long Position;
+ public bool ExState;
+
+ public bool HasExclusiveAccess(long position)
+ {
+ return Position == position && ExState;
+ }
+ }
+
+ private Dictionary<int, ArmMonitor> _monitors;
+
+ private ConcurrentDictionary<long, IntPtr> _observedPages;
+
+ public IntPtr Ram { get; private set; }
+
+ private byte* _ramPtr;
+
+ private byte*** _pageTable;
+
+ public event EventHandler<InvalidAccessEventArgs> InvalidAccess;
+
+ public MemoryManager(IntPtr ram)
+ {
+ _monitors = new Dictionary<int, ArmMonitor>();
+
+ _observedPages = new ConcurrentDictionary<long, IntPtr>();
+
+ Ram = ram;
+
+ _ramPtr = (byte*)ram;
+
+ _pageTable = (byte***)Marshal.AllocHGlobal(PtLvl0Size * IntPtr.Size);
+
+ for (int l0 = 0; l0 < PtLvl0Size; l0++)
+ {
+ _pageTable[l0] = null;
+ }
+ }
+
+ public void RemoveMonitor(int core)
+ {
+ lock (_monitors)
+ {
+ ClearExclusive(core);
+
+ _monitors.Remove(core);
+ }
+ }
+
+ public void SetExclusive(int core, long position)
+ {
+ position &= ~ErgMask;
+
+ lock (_monitors)
+ {
+ foreach (ArmMonitor mon in _monitors.Values)
+ {
+ if (mon.Position == position && mon.ExState)
+ {
+ mon.ExState = false;
+ }
+ }
+
+ if (!_monitors.TryGetValue(core, out ArmMonitor threadMon))
+ {
+ threadMon = new ArmMonitor();
+
+ _monitors.Add(core, threadMon);
+ }
+
+ threadMon.Position = position;
+ threadMon.ExState = true;
+ }
+ }
+
+ public bool TestExclusive(int core, long position)
+ {
+ //Note: Any call to this method also should be followed by a
+ //call to ClearExclusiveForStore if this method returns true.
+ position &= ~ErgMask;
+
+ Monitor.Enter(_monitors);
+
+ if (!_monitors.TryGetValue(core, out ArmMonitor threadMon))
+ {
+ return false;
+ }
+
+ bool exState = threadMon.HasExclusiveAccess(position);
+
+ if (!exState)
+ {
+ Monitor.Exit(_monitors);
+ }
+
+ return exState;
+ }
+
+ public void ClearExclusiveForStore(int core)
+ {
+ if (_monitors.TryGetValue(core, out ArmMonitor threadMon))
+ {
+ threadMon.ExState = false;
+ }
+
+ Monitor.Exit(_monitors);
+ }
+
+ public void ClearExclusive(int core)
+ {
+ lock (_monitors)
+ {
+ if (_monitors.TryGetValue(core, out ArmMonitor threadMon))
+ {
+ threadMon.ExState = false;
+ }
+ }
+ }
+
+ public void WriteInt32ToSharedAddr(long position, int value)
+ {
+ long maskedPosition = position & ~ErgMask;
+
+ lock (_monitors)
+ {
+ foreach (ArmMonitor mon in _monitors.Values)
+ {
+ if (mon.Position == maskedPosition && mon.ExState)
+ {
+ mon.ExState = false;
+ }
+ }
+
+ WriteInt32(position, value);
+ }
+ }
+
+ 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 byte ReadByte(long position)
+ {
+ return *((byte*)Translate(position));
+ }
+
+ public ushort ReadUInt16(long position)
+ {
+ return *((ushort*)Translate(position));
+ }
+
+ public uint ReadUInt32(long position)
+ {
+ return *((uint*)Translate(position));
+ }
+
+ public ulong ReadUInt64(long position)
+ {
+ return *((ulong*)Translate(position));
+ }
+
+ public Vector128<float> ReadVector8(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> ReadVector16(long position)
+ {
+ if (Sse2.IsSupported)
+ {
+ return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse2.SetZeroVector128<ushort>(), ReadUInt16(position), 0));
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector128<float> ReadVector32(long position)
+ {
+ if (Sse.IsSupported)
+ {
+ return Sse.LoadScalarVector128((float*)Translate(position));
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector128<float> ReadVector64(long position)
+ {
+ if (Sse2.IsSupported)
+ {
+ return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)Translate(position)));
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector128<float> ReadVector128(long position)
+ {
+ if (Sse.IsSupported)
+ {
+ return Sse.LoadVector128((float*)Translate(position));
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+ }
+
+ public byte[] ReadBytes(long position, long size)
+ {
+ if ((uint)size > int.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException(nameof(size));
+ }
+
+ EnsureRangeIsValid(position, size);
+
+ byte[] data = new byte[size];
+
+ Marshal.Copy((IntPtr)Translate(position), data, 0, (int)size);
+
+ return data;
+ }
+
+ public void ReadBytes(long position, byte[] data, int startIndex, int size)
+ {
+ //Note: This will be moved later.
+ EnsureRangeIsValid(position, (uint)size);
+
+ Marshal.Copy((IntPtr)Translate(position), data, startIndex, size);
+ }
+
+ 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 void WriteByte(long position, byte value)
+ {
+ *((byte*)TranslateWrite(position)) = value;
+ }
+
+ public void WriteUInt16(long position, ushort value)
+ {
+ *((ushort*)TranslateWrite(position)) = value;
+ }
+
+ public void WriteUInt32(long position, uint value)
+ {
+ *((uint*)TranslateWrite(position)) = value;
+ }
+
+ public void WriteUInt64(long position, ulong value)
+ {
+ *((ulong*)TranslateWrite(position)) = value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteVector8(long position, Vector128<float> value)
+ {
+ if (Sse41.IsSupported)
+ {
+ WriteByte(position, Sse41.Extract(Sse.StaticCast<float, byte>(value), 0));
+ }
+ else if (Sse2.IsSupported)
+ {
+ WriteByte(position, (byte)Sse2.Extract(Sse.StaticCast<float, ushort>(value), 0));
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteVector16(long position, Vector128<float> value)
+ {
+ if (Sse2.IsSupported)
+ {
+ WriteUInt16(position, Sse2.Extract(Sse.StaticCast<float, ushort>(value), 0));
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteVector32(long position, Vector128<float> value)
+ {
+ if (Sse.IsSupported)
+ {
+ Sse.StoreScalar((float*)TranslateWrite(position), value);
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteVector64(long position, Vector128<float> value)
+ {
+ if (Sse2.IsSupported)
+ {
+ Sse2.StoreScalar((double*)TranslateWrite(position), Sse.StaticCast<float, double>(value));
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteVector128(long position, Vector128<float> value)
+ {
+ if (Sse.IsSupported)
+ {
+ Sse.Store((float*)TranslateWrite(position), value);
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+ }
+
+ public void WriteBytes(long position, byte[] data)
+ {
+ EnsureRangeIsValid(position, (uint)data.Length);
+
+ Marshal.Copy(data, 0, (IntPtr)TranslateWrite(position), data.Length);
+ }
+
+ public void WriteBytes(long position, byte[] data, int startIndex, int size)
+ {
+ //Note: This will be moved later.
+ //Using Translate instead of TranslateWrite is on purpose.
+ EnsureRangeIsValid(position, (uint)size);
+
+ Marshal.Copy(data, startIndex, (IntPtr)Translate(position), size);
+ }
+
+ public void CopyBytes(long src, long dst, long size)
+ {
+ //Note: This will be moved later.
+ EnsureRangeIsValid(src, size);
+ EnsureRangeIsValid(dst, size);
+
+ byte* srcPtr = Translate(src);
+ byte* dstPtr = TranslateWrite(dst);
+
+ Buffer.MemoryCopy(srcPtr, dstPtr, size, size);
+ }
+
+ public void Map(long va, long pa, long size)
+ {
+ SetPtEntries(va, _ramPtr + pa, size);
+ }
+
+ public void Unmap(long position, long size)
+ {
+ SetPtEntries(position, null, size);
+
+ StopObservingRegion(position, size);
+ }
+
+ public bool IsMapped(long position)
+ {
+ if (!(IsValidPosition(position)))
+ {
+ return false;
+ }
+
+ long l0 = (position >> PtLvl0Bit) & PtLvl0Mask;
+ long l1 = (position >> PtLvl1Bit) & PtLvl1Mask;
+
+ if (_pageTable[l0] == null)
+ {
+ return false;
+ }
+
+ return _pageTable[l0][l1] != null || _observedPages.ContainsKey(position >> PtPageBits);
+ }
+
+ public long GetPhysicalAddress(long virtualAddress)
+ {
+ byte* ptr = Translate(virtualAddress);
+
+ return (long)(ptr - _ramPtr);
+ }
+
+ internal byte* Translate(long position)
+ {
+ long l0 = (position >> PtLvl0Bit) & PtLvl0Mask;
+ long l1 = (position >> PtLvl1Bit) & PtLvl1Mask;
+
+ long old = position;
+
+ byte** lvl1 = _pageTable[l0];
+
+ if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0)
+ {
+ goto Unmapped;
+ }
+
+ if (lvl1 == null)
+ {
+ goto Unmapped;
+ }
+
+ position &= PageMask;
+
+ byte* ptr = lvl1[l1];
+
+ if (ptr == null)
+ {
+ goto Unmapped;
+ }
+
+ return ptr + position;
+
+Unmapped:
+ return HandleNullPte(old);
+ }
+
+ private byte* HandleNullPte(long position)
+ {
+ long key = position >> PtPageBits;
+
+ if (_observedPages.TryGetValue(key, out IntPtr ptr))
+ {
+ return (byte*)ptr + (position & PageMask);
+ }
+
+ InvalidAccess?.Invoke(this, new InvalidAccessEventArgs(position));
+
+ throw new VmmPageFaultException(position);
+ }
+
+ internal byte* TranslateWrite(long position)
+ {
+ long l0 = (position >> PtLvl0Bit) & PtLvl0Mask;
+ long l1 = (position >> PtLvl1Bit) & PtLvl1Mask;
+
+ long old = position;
+
+ byte** lvl1 = _pageTable[l0];
+
+ if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0)
+ {
+ goto Unmapped;
+ }
+
+ if (lvl1 == null)
+ {
+ goto Unmapped;
+ }
+
+ position &= PageMask;
+
+ byte* ptr = lvl1[l1];
+
+ if (ptr == null)
+ {
+ goto Unmapped;
+ }
+
+ return ptr + position;
+
+Unmapped:
+ return HandleNullPteWrite(old);
+ }
+
+ private byte* HandleNullPteWrite(long position)
+ {
+ long key = position >> PtPageBits;
+
+ if (_observedPages.TryGetValue(key, out IntPtr ptr))
+ {
+ SetPtEntry(position, (byte*)ptr);
+
+ return (byte*)ptr + (position & PageMask);
+ }
+
+ InvalidAccess?.Invoke(this, new InvalidAccessEventArgs(position));
+
+ throw new VmmPageFaultException(position);
+ }
+
+ private void SetPtEntries(long va, byte* ptr, long size)
+ {
+ long endPosition = (va + size + PageMask) & ~PageMask;
+
+ while ((ulong)va < (ulong)endPosition)
+ {
+ SetPtEntry(va, ptr);
+
+ va += PageSize;
+
+ if (ptr != null)
+ {
+ ptr += PageSize;
+ }
+ }
+ }
+
+ private void SetPtEntry(long position, byte* ptr)
+ {
+ if (!IsValidPosition(position))
+ {
+ throw new ArgumentOutOfRangeException(nameof(position));
+ }
+
+ long l0 = (position >> PtLvl0Bit) & PtLvl0Mask;
+ long l1 = (position >> PtLvl1Bit) & PtLvl1Mask;
+
+ if (_pageTable[l0] == null)
+ {
+ byte** lvl1 = (byte**)Marshal.AllocHGlobal(PtLvl1Size * IntPtr.Size);
+
+ for (int zl1 = 0; zl1 < PtLvl1Size; zl1++)
+ {
+ lvl1[zl1] = null;
+ }
+
+ Thread.MemoryBarrier();
+
+ _pageTable[l0] = lvl1;
+ }
+
+ _pageTable[l0][l1] = ptr;
+ }
+
+ public (bool[], int) IsRegionModified(long position, long size)
+ {
+ long endPosition = (position + size + PageMask) & ~PageMask;
+
+ position &= ~PageMask;
+
+ size = endPosition - position;
+
+ bool[] modified = new bool[size >> PtPageBits];
+
+ int count = 0;
+
+ lock (_observedPages)
+ {
+ for (int page = 0; page < modified.Length; page++)
+ {
+ byte* ptr = Translate(position);
+
+ if (_observedPages.TryAdd(position >> PtPageBits, (IntPtr)ptr))
+ {
+ modified[page] = true;
+
+ count++;
+ }
+ else
+ {
+ long l0 = (position >> PtLvl0Bit) & PtLvl0Mask;
+ long l1 = (position >> PtLvl1Bit) & PtLvl1Mask;
+
+ byte** lvl1 = _pageTable[l0];
+
+ if (lvl1 != null)
+ {
+ if (modified[page] = lvl1[l1] != null)
+ {
+ count++;
+ }
+ }
+ }
+
+ SetPtEntry(position, null);
+
+ position += PageSize;
+ }
+ }
+
+ return (modified, count);
+ }
+
+ public void StopObservingRegion(long position, long size)
+ {
+ long endPosition = (position + size + PageMask) & ~PageMask;
+
+ while (position < endPosition)
+ {
+ lock (_observedPages)
+ {
+ if (_observedPages.TryRemove(position >> PtPageBits, out IntPtr ptr))
+ {
+ SetPtEntry(position, (byte*)ptr);
+ }
+ }
+
+ position += PageSize;
+ }
+ }
+
+ public IntPtr GetHostAddress(long position, long size)
+ {
+ EnsureRangeIsValid(position, size);
+
+ return (IntPtr)Translate(position);
+ }
+
+ internal void EnsureRangeIsValid(long position, long size)
+ {
+ long endPos = position + size;
+
+ position &= ~PageMask;
+
+ long expectedPa = GetPhysicalAddress(position);
+
+ while ((ulong)position < (ulong)endPos)
+ {
+ long pa = GetPhysicalAddress(position);
+
+ if (pa != expectedPa)
+ {
+ throw new VmmAccessException(position, size);
+ }
+
+ position += PageSize;
+ expectedPa += PageSize;
+ }
+ }
+
+ public bool IsValidPosition(long position)
+ {
+ return position >> (PtLvl0Bits + PtLvl1Bits + PtPageBits) == 0;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_pageTable == null)
+ {
+ return;
+ }
+
+ for (int l0 = 0; l0 < PtLvl0Size; l0++)
+ {
+ if (_pageTable[l0] != null)
+ {
+ Marshal.FreeHGlobal((IntPtr)_pageTable[l0]);
+ }
+
+ _pageTable[l0] = null;
+ }
+
+ Marshal.FreeHGlobal((IntPtr)_pageTable);
+
+ _pageTable = null;
+ }
+ }
+} \ No newline at end of file