diff options
author | Alex Barney <thealexbarney@gmail.com> | 2018-10-30 19:43:02 -0600 |
---|---|---|
committer | gdkchan <gab.dark.100@gmail.com> | 2018-10-30 22:43:02 -0300 |
commit | 9cb57fb4bb3bbae0ae052a5af4a96a49fc5d864d (patch) | |
tree | 0c97425aeb311c142bc92a6fcc503cb2c07d4376 /ChocolArm64/Memory/MemoryManager.cs | |
parent | 5a87e58183578f5b84ca8d01cbb76aed11820f78 (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/MemoryManager.cs')
-rw-r--r-- | ChocolArm64/Memory/MemoryManager.cs | 745 |
1 files changed, 745 insertions, 0 deletions
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 |