diff options
Diffstat (limited to 'src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs')
-rw-r--r-- | src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs | 338 |
1 files changed, 16 insertions, 322 deletions
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs index 0b6ba260..f410d02e 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; -using System.Threading; namespace Ryujinx.Cpu.Jit { @@ -15,21 +14,6 @@ namespace Ryujinx.Cpu.Jit /// </summary> public sealed class MemoryManagerHostMapped : VirtualMemoryManagerRefCountedBase<ulong, ulong>, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock { - public const int PageToPteShift = 5; // 32 pages (2 bits each) in one ulong page table entry. - public const ulong BlockMappedMask = 0x5555555555555555; // First bit of each table entry set. - - private enum HostMappedPtBits : ulong - { - Unmapped = 0, - Mapped, - WriteTracked, - ReadWriteTracked, - - MappedReplicated = 0x5555555555555555, - WriteTrackedReplicated = 0xaaaaaaaaaaaaaaaa, - ReadWriteTrackedReplicated = ulong.MaxValue, - } - private readonly InvalidAccessHandler _invalidAccessHandler; private readonly bool _unsafeMode; @@ -39,7 +23,7 @@ namespace Ryujinx.Cpu.Jit private readonly MemoryEhMeilleure _memoryEh; - private readonly ulong[] _pageBitmap; + private readonly ManagedPageFlags _pages; /// <inheritdoc/> public bool Supports4KBPages => MemoryBlock.GetPageSize() == PageSize; @@ -81,7 +65,7 @@ namespace Ryujinx.Cpu.Jit AddressSpaceBits = asBits; - _pageBitmap = new ulong[1 << (AddressSpaceBits - (PageBits + PageToPteShift))]; + _pages = new ManagedPageFlags(AddressSpaceBits); Tracking = new MemoryTracking(this, (int)MemoryBlock.GetPageSize(), invalidAccessHandler); _memoryEh = new MemoryEhMeilleure(_addressSpace.Base, _addressSpace.Mirror, Tracking); @@ -94,7 +78,7 @@ namespace Ryujinx.Cpu.Jit /// <param name="size">Size of the range in bytes</param> private void AssertMapped(ulong va, ulong size) { - if (!ValidateAddressAndSize(va, size) || !IsRangeMappedImpl(va, size)) + if (!ValidateAddressAndSize(va, size) || !_pages.IsRangeMapped(va, size)) { throw new InvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}"); } @@ -106,7 +90,7 @@ namespace Ryujinx.Cpu.Jit AssertValidAddressAndSize(va, size); _addressSpace.Map(va, pa, size, flags); - AddMapping(va, size); + _pages.AddMapping(va, size); PtMap(va, pa, size); Tracking.Map(va, size); @@ -126,7 +110,7 @@ namespace Ryujinx.Cpu.Jit UnmapEvent?.Invoke(va, size); Tracking.Unmap(va, size); - RemoveMapping(va, size); + _pages.RemoveMapping(va, size); PtUnmap(va, size); _addressSpace.Unmap(va, size); } @@ -337,22 +321,7 @@ namespace Ryujinx.Cpu.Jit [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsMapped(ulong va) { - return ValidateAddress(va) && IsMappedImpl(va); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool IsMappedImpl(ulong va) - { - ulong page = va >> PageBits; - - int bit = (int)((page & 31) << 1); - - int pageIndex = (int)(page >> PageToPteShift); - ref ulong pageRef = ref _pageBitmap[pageIndex]; - - ulong pte = Volatile.Read(ref pageRef); - - return ((pte >> bit) & 3) != 0; + return ValidateAddress(va) && _pages.IsMapped(va); } /// <inheritdoc/> @@ -360,58 +329,7 @@ namespace Ryujinx.Cpu.Jit { AssertValidAddressAndSize(va, size); - return IsRangeMappedImpl(va, size); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void GetPageBlockRange(ulong pageStart, ulong pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex) - { - startMask = ulong.MaxValue << ((int)(pageStart & 31) << 1); - endMask = ulong.MaxValue >> (64 - ((int)(pageEnd & 31) << 1)); - - pageIndex = (int)(pageStart >> PageToPteShift); - pageEndIndex = (int)((pageEnd - 1) >> PageToPteShift); - } - - private bool IsRangeMappedImpl(ulong va, ulong size) - { - int pages = GetPagesCount(va, size, out _); - - if (pages == 1) - { - return IsMappedImpl(va); - } - - ulong pageStart = va >> PageBits; - ulong pageEnd = pageStart + (ulong)pages; - - GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex); - - // Check if either bit in each 2 bit page entry is set. - // OR the block with itself shifted down by 1, and check the first bit of each entry. - - ulong mask = BlockMappedMask & startMask; - - while (pageIndex <= pageEndIndex) - { - if (pageIndex == pageEndIndex) - { - mask &= endMask; - } - - ref ulong pageRef = ref _pageBitmap[pageIndex++]; - ulong pte = Volatile.Read(ref pageRef); - - pte |= pte >> 1; - if ((pte & mask) != mask) - { - return false; - } - - mask = BlockMappedMask; - } - - return true; + return _pages.IsRangeMapped(va, size); } /// <inheritdoc/> @@ -486,76 +404,7 @@ namespace Ryujinx.Cpu.Jit return; } - // Software table, used for managed memory tracking. - - int pages = GetPagesCount(va, size, out _); - ulong pageStart = va >> PageBits; - - if (pages == 1) - { - ulong tag = (ulong)(write ? HostMappedPtBits.WriteTracked : HostMappedPtBits.ReadWriteTracked); - - int bit = (int)((pageStart & 31) << 1); - - int pageIndex = (int)(pageStart >> PageToPteShift); - ref ulong pageRef = ref _pageBitmap[pageIndex]; - - ulong pte = Volatile.Read(ref pageRef); - ulong state = ((pte >> bit) & 3); - - if (state >= tag) - { - Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); - return; - } - else if (state == 0) - { - ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}"); - } - } - else - { - ulong pageEnd = pageStart + (ulong)pages; - - GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex); - - ulong mask = startMask; - - ulong anyTrackingTag = (ulong)HostMappedPtBits.WriteTrackedReplicated; - - while (pageIndex <= pageEndIndex) - { - if (pageIndex == pageEndIndex) - { - mask &= endMask; - } - - ref ulong pageRef = ref _pageBitmap[pageIndex++]; - - ulong pte = Volatile.Read(ref pageRef); - ulong mappedMask = mask & BlockMappedMask; - - ulong mappedPte = pte | (pte >> 1); - if ((mappedPte & mappedMask) != mappedMask) - { - ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}"); - } - - pte &= mask; - if ((pte & anyTrackingTag) != 0) // Search for any tracking. - { - // Writes trigger any tracking. - // Only trigger tracking from reads if both bits are set on any page. - if (write || (pte & (pte >> 1) & BlockMappedMask) != 0) - { - Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); - break; - } - } - - mask = ulong.MaxValue; - } - } + _pages.SignalMemoryTracking(Tracking, va, size, write, exemptId); } /// <summary> @@ -582,103 +431,28 @@ namespace Ryujinx.Cpu.Jit } /// <inheritdoc/> - public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection) + public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest) { - // Protection is inverted on software pages, since the default value is 0. - protection = (~protection) & MemoryPermission.ReadAndWrite; - - int pages = GetPagesCount(va, size, out va); - ulong pageStart = va >> PageBits; - - if (pages == 1) + if (guest) { - ulong protTag = protection switch - { - MemoryPermission.None => (ulong)HostMappedPtBits.Mapped, - MemoryPermission.Write => (ulong)HostMappedPtBits.WriteTracked, - _ => (ulong)HostMappedPtBits.ReadWriteTracked, - }; - - int bit = (int)((pageStart & 31) << 1); - - ulong tagMask = 3UL << bit; - ulong invTagMask = ~tagMask; - - ulong tag = protTag << bit; - - int pageIndex = (int)(pageStart >> PageToPteShift); - ref ulong pageRef = ref _pageBitmap[pageIndex]; - - ulong pte; - - do - { - pte = Volatile.Read(ref pageRef); - } - while ((pte & tagMask) != 0 && Interlocked.CompareExchange(ref pageRef, (pte & invTagMask) | tag, pte) != pte); + _addressSpace.Base.Reprotect(va, size, protection, false); } else { - ulong pageEnd = pageStart + (ulong)pages; - - GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex); - - ulong mask = startMask; - - ulong protTag = protection switch - { - MemoryPermission.None => (ulong)HostMappedPtBits.MappedReplicated, - MemoryPermission.Write => (ulong)HostMappedPtBits.WriteTrackedReplicated, - _ => (ulong)HostMappedPtBits.ReadWriteTrackedReplicated, - }; - - while (pageIndex <= pageEndIndex) - { - if (pageIndex == pageEndIndex) - { - mask &= endMask; - } - - ref ulong pageRef = ref _pageBitmap[pageIndex++]; - - ulong pte; - ulong mappedMask; - - // Change the protection of all 2 bit entries that are mapped. - do - { - pte = Volatile.Read(ref pageRef); - - mappedMask = pte | (pte >> 1); - mappedMask |= (mappedMask & BlockMappedMask) << 1; - mappedMask &= mask; // Only update mapped pages within the given range. - } - while (Interlocked.CompareExchange(ref pageRef, (pte & (~mappedMask)) | (protTag & mappedMask), pte) != pte); - - mask = ulong.MaxValue; - } + _pages.TrackingReprotect(va, size, protection); } - - protection = protection switch - { - MemoryPermission.None => MemoryPermission.ReadAndWrite, - MemoryPermission.Write => MemoryPermission.Read, - _ => MemoryPermission.None, - }; - - _addressSpace.Base.Reprotect(va, size, protection, false); } /// <inheritdoc/> - public RegionHandle BeginTracking(ulong address, ulong size, int id) + public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags = RegionFlags.None) { - return Tracking.BeginTracking(address, size, id); + return Tracking.BeginTracking(address, size, id, flags); } /// <inheritdoc/> - public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) + public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id, RegionFlags flags = RegionFlags.None) { - return Tracking.BeginGranularTracking(address, size, handles, granularity, id); + return Tracking.BeginGranularTracking(address, size, handles, granularity, id, flags); } /// <inheritdoc/> @@ -688,86 +462,6 @@ namespace Ryujinx.Cpu.Jit } /// <summary> - /// Adds the given address mapping to the page table. - /// </summary> - /// <param name="va">Virtual memory address</param> - /// <param name="size">Size to be mapped</param> - private void AddMapping(ulong va, ulong size) - { - int pages = GetPagesCount(va, size, out _); - ulong pageStart = va >> PageBits; - ulong pageEnd = pageStart + (ulong)pages; - - GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex); - - ulong mask = startMask; - - while (pageIndex <= pageEndIndex) - { - if (pageIndex == pageEndIndex) - { - mask &= endMask; - } - - ref ulong pageRef = ref _pageBitmap[pageIndex++]; - - ulong pte; - ulong mappedMask; - - // Map all 2-bit entries that are unmapped. - do - { - pte = Volatile.Read(ref pageRef); - - mappedMask = pte | (pte >> 1); - mappedMask |= (mappedMask & BlockMappedMask) << 1; - mappedMask |= ~mask; // Treat everything outside the range as mapped, thus unchanged. - } - while (Interlocked.CompareExchange(ref pageRef, (pte & mappedMask) | (BlockMappedMask & (~mappedMask)), pte) != pte); - - mask = ulong.MaxValue; - } - } - - /// <summary> - /// Removes the given address mapping from the page table. - /// </summary> - /// <param name="va">Virtual memory address</param> - /// <param name="size">Size to be unmapped</param> - private void RemoveMapping(ulong va, ulong size) - { - int pages = GetPagesCount(va, size, out _); - ulong pageStart = va >> PageBits; - ulong pageEnd = pageStart + (ulong)pages; - - GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex); - - startMask = ~startMask; - endMask = ~endMask; - - ulong mask = startMask; - - while (pageIndex <= pageEndIndex) - { - if (pageIndex == pageEndIndex) - { - mask |= endMask; - } - - ref ulong pageRef = ref _pageBitmap[pageIndex++]; - ulong pte; - - do - { - pte = Volatile.Read(ref pageRef); - } - while (Interlocked.CompareExchange(ref pageRef, pte & mask, pte) != pte); - - mask = 0; - } - } - - /// <summary> /// Disposes of resources used by the memory manager. /// </summary> protected override void Destroy() |